@@ -1068,14 +1068,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
1068
1068
1069
1069
if let calledMemberAccessExpr = node. calledExpression. as ( MemberAccessExprSyntax . self) {
1070
1070
if let base = calledMemberAccessExpr. base, base. is ( DeclReferenceExprSyntax . self) {
1071
- // When this function call is wrapped by a try-expr or await-expr, the group applied when
1072
- // visiting that wrapping expression is sufficient. Adding another group here in that case
1073
- // can result in unnecessarily breaking after the try/await keyword.
1074
- if !( base. firstToken ( viewMode: . sourceAccurate) ? . previousToken ( viewMode: . all) ? . parent? . is ( TryExprSyntax . self)
1075
- ?? false
1076
- || base. firstToken ( viewMode: . sourceAccurate) ? . previousToken ( viewMode: . all) ? . parent? . is ( AwaitExprSyntax . self)
1077
- ?? false )
1078
- {
1071
+ // When this function call is wrapped by a keyword-modified expression, the group applied
1072
+ // when visiting that wrapping expression is sufficient. Adding another group here in that
1073
+ // case can result in unnecessarily breaking after the modifier keyword.
1074
+ if !( base. firstToken ( viewMode: . sourceAccurate) ? . previousToken ( viewMode: . all) ? . parent? . isProtocol (
1075
+ KeywordModifiedExprSyntaxProtocol . self
1076
+ ) ?? false ) {
1079
1077
before ( base. firstToken ( viewMode: . sourceAccurate) , tokens: . open)
1080
1078
after ( calledMemberAccessExpr. declName. baseName. lastToken ( viewMode: . sourceAccurate) , tokens: . close)
1081
1079
}
@@ -1780,8 +1778,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
1780
1778
tokens: . break( . continue, newlines: . elective( ignoresDiscretionary: true ) )
1781
1779
)
1782
1780
1783
- // Check for an anchor token inside of the expression to group with the try keyword.
1784
- if let anchorToken = findTryAwaitExprConnectingToken ( inExpr: node. expression) {
1781
+ // Check for an anchor token inside of the expression to end a group starting with the `try`
1782
+ // keyword.
1783
+ if !( node. parent? . isProtocol ( KeywordModifiedExprSyntaxProtocol . self) ?? false ) ,
1784
+ let anchorToken = connectingTokenForKeywordModifiedExpr ( inSubExpr: node. expression)
1785
+ {
1785
1786
before ( node. tryKeyword, tokens: . open)
1786
1787
after ( anchorToken, tokens: . close)
1787
1788
}
@@ -1795,9 +1796,10 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
1795
1796
tokens: . break( . continue, newlines: . elective( ignoresDiscretionary: true ) )
1796
1797
)
1797
1798
1798
- // Check for an anchor token inside of the expression to group with the await keyword.
1799
- if !( node. parent? . is ( TryExprSyntax . self) ?? false ) ,
1800
- let anchorToken = findTryAwaitExprConnectingToken ( inExpr: node. expression)
1799
+ // Check for an anchor token inside of the expression to end a group starting with the `await`
1800
+ // keyword.
1801
+ if !( node. parent? . isProtocol ( KeywordModifiedExprSyntaxProtocol . self) ?? false ) ,
1802
+ let anchorToken = connectingTokenForKeywordModifiedExpr ( inSubExpr: node. expression)
1801
1803
{
1802
1804
before ( node. awaitKeyword, tokens: . open)
1803
1805
after ( anchorToken, tokens: . close)
@@ -1806,18 +1808,37 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
1806
1808
return . visitChildren
1807
1809
}
1808
1810
1809
- /// Searches the AST from `expr` to find a token that should be grouped with an enclosing
1810
- /// try-expr or await-expr. Returns that token, or nil when no such token is found.
1811
+ override func visit( _ node: UnsafeExprSyntax ) -> SyntaxVisitorContinueKind {
1812
+ // Unlike `try` and `await`, `unsafe` is a contextual keyword that may not be separated from
1813
+ // the following token by a line break. Keep them glued together with `.space`.
1814
+ before ( node. expression. firstToken ( viewMode: . sourceAccurate) , tokens: . space)
1815
+
1816
+ // Check for an anchor token inside of the expression to end a group starting with the `unsafe`
1817
+ // keyword.
1818
+ if !( node. parent? . isProtocol ( KeywordModifiedExprSyntaxProtocol . self) ?? false ) ,
1819
+ let anchorToken = connectingTokenForKeywordModifiedExpr ( inSubExpr: node. expression)
1820
+ {
1821
+ before ( node. unsafeKeyword, tokens: . open)
1822
+ after ( anchorToken, tokens: . close)
1823
+ }
1824
+
1825
+ return . visitChildren
1826
+ }
1827
+
1828
+ /// Searches within a subexpression of a keyword-modified expression to find the last token in a
1829
+ /// range that should be grouped with the leading keyword modifier.
1811
1830
///
1812
- /// - Parameter expr: An expression that is wrapped by a try-expr or await-expr.
1813
- /// - Returns: A token that should be grouped with the try-expr or await-expr, or nil.
1814
- func findTryAwaitExprConnectingToken( inExpr expr: ExprSyntax ) -> TokenSyntax ? {
1815
- if let awaitExpr = expr. as ( AwaitExprSyntax . self) {
1816
- // If we were called from the `try` of a `try await <expr>`, drill into the child expression.
1817
- return findTryAwaitExprConnectingToken ( inExpr: awaitExpr. expression)
1831
+ /// - Parameter expr: An expression that is wrapped by a keyword-modified expression.
1832
+ /// - Returns: The token that should end the group that is started by the modifier keyword, or
1833
+ /// nil if there should be no group.
1834
+ func connectingTokenForKeywordModifiedExpr( inSubExpr expr: ExprSyntax ) -> TokenSyntax ? {
1835
+ if let modifiedExpr = expr. asProtocol ( KeywordModifiedExprSyntaxProtocol . self) {
1836
+ // If we were called from a keyword-modified expression like `try`, `await`, or `unsafe`,
1837
+ // recursively drill into the child expression.
1838
+ return connectingTokenForKeywordModifiedExpr ( inSubExpr: modifiedExpr. expression)
1818
1839
}
1819
1840
if let callingExpr = expr. asProtocol ( CallingExprSyntaxProtocol . self) {
1820
- return findTryAwaitExprConnectingToken ( inExpr : callingExpr. calledExpression)
1841
+ return connectingTokenForKeywordModifiedExpr ( inSubExpr : callingExpr. calledExpression)
1821
1842
}
1822
1843
if let memberAccessExpr = expr. as ( MemberAccessExprSyntax . self) , let base = memberAccessExpr. base {
1823
1844
// When there's a simple base (i.e. identifier), group the entire `try/await <base>.<name>`
@@ -1826,7 +1847,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
1826
1847
if base. is ( DeclReferenceExprSyntax . self) {
1827
1848
return memberAccessExpr. declName. baseName. lastToken ( viewMode: . sourceAccurate)
1828
1849
}
1829
- return findTryAwaitExprConnectingToken ( inExpr : base)
1850
+ return connectingTokenForKeywordModifiedExpr ( inSubExpr : base)
1830
1851
}
1831
1852
if expr. is ( DeclReferenceExprSyntax . self) {
1832
1853
return expr. lastToken ( viewMode: . sourceAccurate)
@@ -3820,13 +3841,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
3820
3841
/// that are known to wrap an expression, e.g. try expressions, are handled by checking the
3821
3842
/// expression that they contain.
3822
3843
private func isCompoundExpression( _ expr: ExprSyntax ) -> Bool {
3844
+ if let modifiedExpr = expr. asProtocol ( KeywordModifiedExprSyntaxProtocol . self) {
3845
+ return isCompoundExpression ( modifiedExpr. expression)
3846
+ }
3823
3847
switch Syntax ( expr) . as ( SyntaxEnum . self) {
3824
- case . awaitExpr( let awaitExpr) :
3825
- return isCompoundExpression ( awaitExpr. expression)
3826
3848
case . infixOperatorExpr, . ternaryExpr, . isExpr, . asExpr:
3827
3849
return true
3828
- case . tryExpr( let tryExpr) :
3829
- return isCompoundExpression ( tryExpr. expression)
3830
3850
case . tupleExpr( let tupleExpr) where tupleExpr. elements. count == 1 :
3831
3851
return isCompoundExpression ( tupleExpr. elements. first!. expression)
3832
3852
default :
@@ -4500,29 +4520,3 @@ extension NewlineBehavior {
4500
4520
}
4501
4521
}
4502
4522
}
4503
-
4504
- /// Common protocol implemented by expression syntax types that support calling another expression.
4505
- protocol CallingExprSyntaxProtocol : ExprSyntaxProtocol {
4506
- var calledExpression : ExprSyntax { get }
4507
- }
4508
-
4509
- extension FunctionCallExprSyntax : CallingExprSyntaxProtocol { }
4510
- extension SubscriptCallExprSyntax : CallingExprSyntaxProtocol { }
4511
-
4512
- extension Syntax {
4513
- func asProtocol( _: CallingExprSyntaxProtocol . Protocol ) -> CallingExprSyntaxProtocol ? {
4514
- return self . asProtocol ( SyntaxProtocol . self) as? CallingExprSyntaxProtocol
4515
- }
4516
- func isProtocol( _: CallingExprSyntaxProtocol . Protocol ) -> Bool {
4517
- return self . asProtocol ( CallingExprSyntaxProtocol . self) != nil
4518
- }
4519
- }
4520
-
4521
- extension ExprSyntax {
4522
- func asProtocol( _: CallingExprSyntaxProtocol . Protocol ) -> CallingExprSyntaxProtocol ? {
4523
- return Syntax ( self ) . asProtocol ( SyntaxProtocol . self) as? CallingExprSyntaxProtocol
4524
- }
4525
- func isProtocol( _: CallingExprSyntaxProtocol . Protocol ) -> Bool {
4526
- return self . asProtocol ( CallingExprSyntaxProtocol . self) != nil
4527
- }
4528
- }
0 commit comments