Skip to content

Commit 719ecd5

Browse files
authored
Merge pull request #258 from allevato/xcode13b2
Support Swift 5.5 concurrency syntax.
2 parents 773b995 + 35e8687 commit 719ecd5

File tree

9 files changed

+546
-38
lines changed

9 files changed

+546
-38
lines changed

Sources/SwiftFormatPrettyPrint/SequenceExprFolding.swift

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,11 @@ extension SequenceExprSyntax {
9696

9797
case 3:
9898
// A sequence with three elements will not be changed by folding unless
99-
// it contains a cast expression, ternary, or `try`. (This may be more
100-
// inclusive than it needs to be.)
99+
// it contains a cast expression, ternary, `await`, or `try`. (This may
100+
// be more inclusive than it needs to be.)
101101
return elements.contains {
102-
$0.is(AsExprSyntax.self) || $0.is(IsExprSyntax.self) || $0.is(TernaryExprSyntax.self)
102+
$0.is(AsExprSyntax.self) || $0.is(IsExprSyntax.self)
103+
|| $0.is(TernaryExprSyntax.self) || $0.is(AwaitExprSyntax.self)
103104
|| $0.is(TryExprSyntax.self)
104105
}
105106

@@ -331,8 +332,8 @@ extension SequenceExprSyntax {
331332
/// This function takes into account certain corrections that must occur as
332333
/// part of folding, like repairing ternary and cast expressions (undoing the
333334
/// even/odd normalization that was performed at the beginning of the
334-
/// algorithm), as well as absorbing other operators and operands into `try`
335-
/// expressions.
335+
/// algorithm), as well as absorbing other operators and operands into
336+
/// `await/try` expressions.
336337
private func makeExpression(
337338
operator op: ExprSyntax,
338339
lhs: ExprSyntax,
@@ -341,31 +342,39 @@ extension SequenceExprSyntax {
341342
) -> ExprSyntax {
342343
var lhs = lhs
343344

344-
// If the left-hand side is a `try`, hoist it up. The compiler will parse an
345-
// expression like `try foo() + 1` syntactically as
346-
// `SequenceExpr(TryExpr(foo()), +, 1)`, then fold the rest of the
347-
// expression into the `try` as `TryExpr(BinaryExpr(foo(), +, 1))`. So, we
348-
// temporarily drop down to the subexpression for the purposes of this
349-
// function, then before returning below, we wrap the result back in the
350-
// `try`.
345+
// If the left-hand side is a `try` or `await`, hoist it up. The compiler
346+
// will parse an expression like `try|await foo() + 1` syntactically as
347+
// `SequenceExpr(TryExpr|AwaitExpr(foo()), +, 1)`, then fold the rest of
348+
// the expression into the `try|await` as
349+
// `TryExpr|AwaitExpr(BinaryExpr(foo(), +, 1))`. So, we temporarily drop
350+
// down to the subexpression for the purposes of this function, then before
351+
// returning below, we wrap the result back in the `try|await`.
351352
//
352-
// If the right-hand side is a `try`, it's an error unless the operator is
353-
// an assignment or ternary operator and there's nothing to the right that
354-
// didn't parse as part of the right operand. The compiler handles that case
355-
// so that it can emit an error, but for the purposes of the syntax tree, we
356-
// can leave it alone.
353+
// If the right-hand side is a `try` or `await`, it's an error unless the
354+
// operator is an assignment or ternary operator and there's nothing to the
355+
// right that didn't parse as part of the right operand. The compiler
356+
// handles that case so that it can emit an error, but for the purposes of
357+
// the syntax tree, we can leave it alone.
357358
let maybeTryExpr = lhs.as(TryExprSyntax.self)
358359
if let tryExpr = maybeTryExpr {
359360
lhs = tryExpr.expression
360361
}
362+
let maybeAwaitExpr = lhs.as(AwaitExprSyntax.self)
363+
if let awaitExpr = maybeAwaitExpr {
364+
lhs = awaitExpr.expression
365+
}
361366

362367
let makeResultExpression = { (expr: ExprSyntax) -> ExprSyntax in
363-
// Fold the result back into the `try` if it was present; otherwise, just
364-
// return the result itself.
368+
// Fold the result back into the `try` and/or `await` if either were
369+
// present; otherwise, just return the result itself.
370+
var result = expr
371+
if let awaitExpr = maybeAwaitExpr {
372+
result = ExprSyntax(awaitExpr.withExpression(result))
373+
}
365374
if let tryExpr = maybeTryExpr {
366-
return ExprSyntax(tryExpr.withExpression(expr))
375+
result = ExprSyntax(tryExpr.withExpression(result))
367376
}
368-
return expr
377+
return result
369378
}
370379

371380
switch Syntax(op).as(SyntaxEnum.self) {

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,24 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
519519

520520
override func visit(_ node: ForInStmtSyntax) -> SyntaxVisitorContinueKind {
521521
after(node.labelColon, tokens: .space)
522-
after(node.forKeyword, tokens: .space)
522+
523+
// If we have a `(try) await` clause, allow breaking after the `for` so that the `(try) await`
524+
// can fall onto the next line if needed, and if both `try await` are present, keep them
525+
// together. Otherwise, keep `for` glued to the token after it so that we break somewhere later
526+
// on the line.
527+
if let awaitKeyword = node.awaitKeyword {
528+
after(node.forKeyword, tokens: .break)
529+
if let tryKeyword = node.tryKeyword {
530+
before(tryKeyword, tokens: .open)
531+
after(tryKeyword, tokens: .break)
532+
after(awaitKeyword, tokens: .close, .break)
533+
} else {
534+
after(awaitKeyword, tokens: .break)
535+
}
536+
} else {
537+
after(node.forKeyword, tokens: .space)
538+
}
539+
523540
after(node.caseKeyword, tokens: .space)
524541
before(node.inKeyword, tokens: .break)
525542
after(node.inKeyword, tokens: .space)
@@ -897,10 +914,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
897914

898915
if let calledMemberAccessExpr = node.calledExpression.as(MemberAccessExprSyntax.self) {
899916
if let base = calledMemberAccessExpr.base, base.is(IdentifierExprSyntax.self) {
900-
// When this function call is wrapped by a try-expr, the group applied when visiting the
901-
// try-expr is sufficient. Adding another gruop here in that case can result in
902-
// unnecessarily breaking after the try keyword.
903-
if !(base.firstToken?.previousToken?.parent?.is(TryExprSyntax.self) ?? false) {
917+
// When this function call is wrapped by a try-expr or await-expr, the group applied when
918+
// visiting that wrapping expression is sufficient. Adding another group here in that case
919+
// can result in unnecessarily breaking after the try/await keyword.
920+
if !(base.firstToken?.previousToken?.parent?.is(TryExprSyntax.self) ?? false
921+
|| base.firstToken?.previousToken?.parent?.is(AwaitExprSyntax.self) ?? false) {
904922
before(base.firstToken, tokens: .open)
905923
after(calledMemberAccessExpr.name.lastToken, tokens: .close)
906924
}
@@ -1050,6 +1068,8 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
10501068
override func visit(_ node: ClosureSignatureSyntax) -> SyntaxVisitorContinueKind {
10511069
before(node.firstToken, tokens: .open)
10521070

1071+
arrangeAttributeList(node.attributes, suppressFinalBreak: node.input == nil)
1072+
10531073
if let input = node.input {
10541074
// We unconditionally put a break before the `in` keyword below, so we should only put a break
10551075
// after the capture list's right bracket if there are arguments following it or we'll end up
@@ -1087,7 +1107,13 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
10871107
}
10881108
}
10891109

1110+
before(node.asyncKeyword, tokens: .break)
10901111
before(node.throwsTok, tokens: .break)
1112+
if let asyncKeyword = node.asyncKeyword, let throwsTok = node.throwsTok {
1113+
before(asyncKeyword, tokens: .open)
1114+
after(throwsTok, tokens: .close)
1115+
}
1116+
10911117
before(node.output?.arrow, tokens: .break)
10921118
after(node.lastToken, tokens: .close)
10931119
before(node.inTok, tokens: .break(.same))
@@ -1461,32 +1487,52 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
14611487
tokens: .break(.continue, newlines: .elective(ignoresDiscretionary: true)))
14621488

14631489
// Check for an anchor token inside of the expression to group with the try keyword.
1464-
if let anchorToken = findTryExprConnectingToken(inExpr: node.expression) {
1490+
if let anchorToken = findTryAwaitExprConnectingToken(inExpr: node.expression) {
14651491
before(node.tryKeyword, tokens: .open)
14661492
after(anchorToken, tokens: .close)
14671493
}
14681494

14691495
return .visitChildren
14701496
}
14711497

1498+
override func visit(_ node: AwaitExprSyntax) -> SyntaxVisitorContinueKind {
1499+
before(
1500+
node.expression.firstToken,
1501+
tokens: .break(.continue, newlines: .elective(ignoresDiscretionary: true)))
1502+
1503+
// Check for an anchor token inside of the expression to group with the await keyword.
1504+
if !(node.parent?.is(TryExprSyntax.self) ?? false),
1505+
let anchorToken = findTryAwaitExprConnectingToken(inExpr: node.expression)
1506+
{
1507+
before(node.awaitKeyword, tokens: .open)
1508+
after(anchorToken, tokens: .close)
1509+
}
1510+
1511+
return .visitChildren
1512+
}
1513+
14721514
/// Searches the AST from `expr` to find a token that should be grouped with an enclosing
1473-
/// try-expr. Returns that token, or nil when no such token is found.
1515+
/// try-expr or await-expr. Returns that token, or nil when no such token is found.
14741516
///
1475-
/// - Parameter expr: An expression that is wrapped by a try-expr.
1476-
/// - Returns: A token that should be grouped with the try-expr, or nil.
1477-
func findTryExprConnectingToken(inExpr expr: ExprSyntax) -> TokenSyntax? {
1517+
/// - Parameter expr: An expression that is wrapped by a try-expr or await-expr.
1518+
/// - Returns: A token that should be grouped with the try-expr or await-expr, or nil.
1519+
func findTryAwaitExprConnectingToken(inExpr expr: ExprSyntax) -> TokenSyntax? {
1520+
if let awaitExpr = expr.as(AwaitExprSyntax.self) {
1521+
// If we were called from the `try` of a `try await <expr>`, drill into the child expression.
1522+
return findTryAwaitExprConnectingToken(inExpr: awaitExpr.expression)
1523+
}
14781524
if let callingExpr = expr.asProtocol(CallingExprSyntaxProtocol.self) {
1479-
return findTryExprConnectingToken(inExpr: callingExpr.calledExpression)
1525+
return findTryAwaitExprConnectingToken(inExpr: callingExpr.calledExpression)
14801526
}
14811527
if let memberAccessExpr = expr.as(MemberAccessExprSyntax.self), let base = memberAccessExpr.base
14821528
{
1483-
// When there's a simple base (i.e. identifier), group the entire `try <base>.<name>`
1529+
// When there's a simple base (i.e. identifier), group the entire `try/await <base>.<name>`
14841530
// sequence. This check has to happen here so that the `MemberAccessExprSyntax.name` is
14851531
// available.
14861532
if base.is(IdentifierExprSyntax.self) {
14871533
return memberAccessExpr.name.lastToken
14881534
}
1489-
return findTryExprConnectingToken(inExpr: base)
1535+
return findTryAwaitExprConnectingToken(inExpr: base)
14901536
}
14911537
if expr.is(IdentifierExprSyntax.self) {
14921538
return expr.lastToken
@@ -1634,13 +1680,28 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
16341680
}
16351681

16361682
override func visit(_ node: DeclModifierSyntax) -> SyntaxVisitorContinueKind {
1637-
after(node.lastToken, tokens: .break)
1683+
// Due to the way we currently use spaces after let/var keywords in variable bindings, we need
1684+
// this special exception for `async let` statements to avoid breaking prematurely between the
1685+
// `async` and `let` keywords.
1686+
let breakOrSpace: Token
1687+
if node.name.tokenKind == .identifier("async") {
1688+
breakOrSpace = .space
1689+
} else {
1690+
breakOrSpace = .break
1691+
}
1692+
after(node.lastToken, tokens: breakOrSpace)
16381693
return .visitChildren
16391694
}
16401695

16411696
override func visit(_ node: FunctionSignatureSyntax) -> SyntaxVisitorContinueKind {
16421697
before(node.asyncOrReasyncKeyword, tokens: .break)
16431698
before(node.throwsOrRethrowsKeyword, tokens: .break)
1699+
if let asyncOrReasyncKeyword = node.asyncOrReasyncKeyword,
1700+
let throwsOrRethrowsKeyword = node.throwsOrRethrowsKeyword
1701+
{
1702+
before(asyncOrReasyncKeyword, tokens: .open)
1703+
after(throwsOrRethrowsKeyword, tokens: .close)
1704+
}
16441705
before(node.output?.firstToken, tokens: .break)
16451706
return .visitChildren
16461707
}
@@ -2472,11 +2533,18 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
24722533
// MARK: - Various other helper methods
24732534

24742535
/// Applies formatting tokens around and between the attributes in an attribute list.
2475-
private func arrangeAttributeList(_ attributes: AttributeListSyntax?) {
2536+
private func arrangeAttributeList(
2537+
_ attributes: AttributeListSyntax?,
2538+
suppressFinalBreak: Bool = false
2539+
) {
24762540
if let attributes = attributes {
24772541
before(attributes.firstToken, tokens: .open)
24782542
insertTokens(.break(.same), betweenElementsOf: attributes)
2479-
after(attributes.lastToken, tokens: .close, .break(.same))
2543+
var afterAttributeTokens = [Token.close]
2544+
if !suppressFinalBreak {
2545+
afterAttributeTokens.append(.break(.same))
2546+
}
2547+
after(attributes.lastToken, tokens: afterAttributeTokens)
24802548
}
24812549
}
24822550

@@ -3087,10 +3155,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
30873155
}
30883156

30893157
/// Returns whether the given expression consists of multiple subexpressions. Certain expressions
3090-
/// that are known to wrap an expressions, e.g. try expressions, are handled by checking the
3158+
/// that are known to wrap an expression, e.g. try expressions, are handled by checking the
30913159
/// expression that they contain.
30923160
private func isCompoundExpression(_ expr: ExprSyntax) -> Bool {
30933161
switch Syntax(expr).as(SyntaxEnum.self) {
3162+
case .awaitExpr(let awaitExpr):
3163+
return isCompoundExpression(awaitExpr.expression)
30943164
case .sequenceExpr(let sequenceExpr):
30953165
return sequenceExpr.elements.count > 1
30963166
case .ternaryExpr:

0 commit comments

Comments
 (0)