@@ -1068,14 +1068,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
10681068
10691069 if let calledMemberAccessExpr = node. calledExpression. as ( MemberAccessExprSyntax . self) {
10701070 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 ) {
10791077 before ( base. firstToken ( viewMode: . sourceAccurate) , tokens: . open)
10801078 after ( calledMemberAccessExpr. declName. baseName. lastToken ( viewMode: . sourceAccurate) , tokens: . close)
10811079 }
@@ -1780,8 +1778,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
17801778 tokens: . break( . continue, newlines: . elective( ignoresDiscretionary: true ) )
17811779 )
17821780
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+ {
17851786 before ( node. tryKeyword, tokens: . open)
17861787 after ( anchorToken, tokens: . close)
17871788 }
@@ -1795,9 +1796,10 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
17951796 tokens: . break( . continue, newlines: . elective( ignoresDiscretionary: true ) )
17961797 )
17971798
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)
18011803 {
18021804 before ( node. awaitKeyword, tokens: . open)
18031805 after ( anchorToken, tokens: . close)
@@ -1806,18 +1808,37 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
18061808 return . visitChildren
18071809 }
18081810
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.
18111830 ///
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)
18181839 }
18191840 if let callingExpr = expr. asProtocol ( CallingExprSyntaxProtocol . self) {
1820- return findTryAwaitExprConnectingToken ( inExpr : callingExpr. calledExpression)
1841+ return connectingTokenForKeywordModifiedExpr ( inSubExpr : callingExpr. calledExpression)
18211842 }
18221843 if let memberAccessExpr = expr. as ( MemberAccessExprSyntax . self) , let base = memberAccessExpr. base {
18231844 // 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 {
18261847 if base. is ( DeclReferenceExprSyntax . self) {
18271848 return memberAccessExpr. declName. baseName. lastToken ( viewMode: . sourceAccurate)
18281849 }
1829- return findTryAwaitExprConnectingToken ( inExpr : base)
1850+ return connectingTokenForKeywordModifiedExpr ( inSubExpr : base)
18301851 }
18311852 if expr. is ( DeclReferenceExprSyntax . self) {
18321853 return expr. lastToken ( viewMode: . sourceAccurate)
@@ -3825,13 +3846,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
38253846 /// that are known to wrap an expression, e.g. try expressions, are handled by checking the
38263847 /// expression that they contain.
38273848 private func isCompoundExpression( _ expr: ExprSyntax ) -> Bool {
3849+ if let modifiedExpr = expr. asProtocol ( KeywordModifiedExprSyntaxProtocol . self) {
3850+ return isCompoundExpression ( modifiedExpr. expression)
3851+ }
38283852 switch Syntax ( expr) . as ( SyntaxEnum . self) {
3829- case . awaitExpr( let awaitExpr) :
3830- return isCompoundExpression ( awaitExpr. expression)
38313853 case . infixOperatorExpr, . ternaryExpr, . isExpr, . asExpr:
38323854 return true
3833- case . tryExpr( let tryExpr) :
3834- return isCompoundExpression ( tryExpr. expression)
38353855 case . tupleExpr( let tupleExpr) where tupleExpr. elements. count == 1 :
38363856 return isCompoundExpression ( tupleExpr. elements. first!. expression)
38373857 default :
@@ -4505,29 +4525,3 @@ extension NewlineBehavior {
45054525 }
45064526 }
45074527}
4508-
4509- /// Common protocol implemented by expression syntax types that support calling another expression.
4510- protocol CallingExprSyntaxProtocol : ExprSyntaxProtocol {
4511- var calledExpression : ExprSyntax { get }
4512- }
4513-
4514- extension FunctionCallExprSyntax : CallingExprSyntaxProtocol { }
4515- extension SubscriptCallExprSyntax : CallingExprSyntaxProtocol { }
4516-
4517- extension Syntax {
4518- func asProtocol( _: CallingExprSyntaxProtocol . Protocol ) -> CallingExprSyntaxProtocol ? {
4519- return self . asProtocol ( SyntaxProtocol . self) as? CallingExprSyntaxProtocol
4520- }
4521- func isProtocol( _: CallingExprSyntaxProtocol . Protocol ) -> Bool {
4522- return self . asProtocol ( CallingExprSyntaxProtocol . self) != nil
4523- }
4524- }
4525-
4526- extension ExprSyntax {
4527- func asProtocol( _: CallingExprSyntaxProtocol . Protocol ) -> CallingExprSyntaxProtocol ? {
4528- return Syntax ( self ) . asProtocol ( SyntaxProtocol . self) as? CallingExprSyntaxProtocol
4529- }
4530- func isProtocol( _: CallingExprSyntaxProtocol . Protocol ) -> Bool {
4531- return self . asProtocol ( CallingExprSyntaxProtocol . self) != nil
4532- }
4533- }
0 commit comments