@@ -43,7 +43,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
4343
4444 /// Lists the expressions that have been visited, from the outermost expression, where contextual
4545 /// breaks and start/end contextual breaking tokens have been inserted.
46- private var preVisitedExprs = Set < ExprSyntax > ( )
46+ private var preVisitedExprs = Set < SyntaxIdentifier > ( )
47+
48+ /// Tracks the "root" exprs where previsiting for contextual breaks started so that
49+ /// `preVisitedExprs` can be emptied after exiting an expr tree.
50+ private var rootExprs = Set < SyntaxIdentifier > ( )
4751
4852 /// Lists the tokens that are the closing or final delimiter of a node that shouldn't be split
4953 /// from the preceding token. When breaks are inserted around compound expressions, the breaks are
@@ -850,6 +854,10 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
850854 return . visitChildren
851855 }
852856
857+ override func visitPost( _ node: MemberAccessExprSyntax ) {
858+ clearContextualBreakState ( node)
859+ }
860+
853861 override func visit( _ node: FunctionCallExprSyntax ) -> SyntaxVisitorContinueKind {
854862 preVisitInsertingContextualBreaks ( node)
855863
@@ -881,6 +889,10 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
881889 return . visitChildren
882890 }
883891
892+ override func visitPost( _ node: FunctionCallExprSyntax ) {
893+ clearContextualBreakState ( node)
894+ }
895+
884896 /// Arrange the given argument list (or equivalently, tuple expression list) as a list of function
885897 /// arguments.
886898 ///
@@ -1076,6 +1088,10 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
10761088 return . visitChildren
10771089 }
10781090
1091+ override func visitPost( _ node: SubscriptExprSyntax ) {
1092+ clearContextualBreakState ( node)
1093+ }
1094+
10791095 override func visit( _ node: ExpressionSegmentSyntax ) -> SyntaxVisitorContinueKind {
10801096 // TODO: For now, just use the raw text of the node and don't try to format it deeper. In the
10811097 // future, we should find a way to format the expression but without wrapping so that at least
@@ -2271,7 +2287,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
22712287
22722288 /// Appends the before-tokens of the given syntax token to the token stream.
22732289 private func appendBeforeTokens( _ token: TokenSyntax ) {
2274- if let before = beforeMap [ token] {
2290+ if let before = beforeMap. removeValue ( forKey : token) {
22752291 before. forEach ( appendToken)
22762292 }
22772293 }
@@ -2331,7 +2347,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
23312347 /// not incorrectly inserted into the token stream *after* a break or newline.
23322348 private func appendAfterTokensAndTrailingComments( _ token: TokenSyntax ) {
23332349 let ( wasLineComment, trailingCommentTokens) = afterTokensForTrailingComment ( token)
2334- let afterGroups = afterMap [ token] ?? [ ]
2350+ let afterGroups = afterMap. removeValue ( forKey : token) ?? [ ]
23352351 var hasAppendedTrailingComment = false
23362352
23372353 if !wasLineComment {
@@ -3177,23 +3193,34 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
31773193 appendToken ( . break( . same, size: 0 ) )
31783194 }
31793195
3196+ /// Cleans up state related to inserting contextual breaks throughout expressions during
3197+ /// `visitPost` for an expression that is the root of an expression tree.
3198+ private func clearContextualBreakState< T: ExprSyntaxProtocol > ( _ expr: T ) {
3199+ let exprID = expr. id
3200+ if rootExprs. remove ( exprID) != nil {
3201+ preVisitedExprs. removeAll ( )
3202+ }
3203+ }
3204+
31803205 /// Visits the given expression node and all of the nested expression nodes, inserting tokens
31813206 /// necessary for contextual breaking throughout the expression. Records the nodes that were
31823207 /// visited so that they can be skipped later.
31833208 private func preVisitInsertingContextualBreaks< T: ExprSyntaxProtocol & Equatable > ( _ expr: T ) {
3184- let exprSyntax = ExprSyntax ( expr)
3185- if !preVisitedExprs. contains ( exprSyntax ) {
3186- let ( visited , _ , _ ) = insertContextualBreaks ( exprSyntax , isTopLevel : true )
3187- preVisitedExprs . formUnion ( visited )
3209+ let exprID = expr. id
3210+ if !preVisitedExprs. contains ( exprID ) {
3211+ rootExprs . insert ( exprID )
3212+ insertContextualBreaks ( ExprSyntax ( expr ) , isTopLevel : true )
31883213 }
31893214 }
31903215
31913216 /// Recursively visits nested expressions from the given expression inserting contextual breaking
31923217 /// tokens. When visiting an expression node, `preVisitInsertingContextualBreaks(_:)` should be
31933218 /// called instead of this helper.
3219+ @discardableResult
31943220 private func insertContextualBreaks( _ expr: ExprSyntax , isTopLevel: Bool ) -> (
3195- [ ExprSyntax ] , hasCompoundExpression: Bool , hasMemberAccess: Bool
3221+ hasCompoundExpression: Bool , hasMemberAccess: Bool
31963222 ) {
3223+ preVisitedExprs. insert ( expr. id)
31973224 if let memberAccessExpr = expr. as ( MemberAccessExprSyntax . self) {
31983225 // When the member access is part of a calling expression, the break before the dot is
31993226 // inserted when visiting the parent node instead so that the break is inserted before any
@@ -3202,21 +3229,18 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
32023229 expr. parent? . isProtocol ( CallingExprSyntaxProtocol . self) != true {
32033230 before ( memberAccessExpr. dot, tokens: . break( . contextual, size: 0 ) )
32043231 }
3205- let children : [ ExprSyntax ]
32063232 var hasCompoundExpression = false
32073233 if let base = memberAccessExpr. base {
3208- ( children, hasCompoundExpression, _) = insertContextualBreaks ( base, isTopLevel: false )
3209- } else {
3210- children = [ ]
3234+ ( hasCompoundExpression, _) = insertContextualBreaks ( base, isTopLevel: false )
32113235 }
32123236 if isTopLevel {
32133237 before ( expr. firstToken, tokens: . contextualBreakingStart)
32143238 after ( expr. lastToken, tokens: . contextualBreakingEnd)
32153239 }
3216- return ( [ expr ] + children , hasCompoundExpression, true )
3240+ return ( hasCompoundExpression, true )
32173241 } else if let callingExpr = expr. asProtocol ( CallingExprSyntaxProtocol . self) {
32183242 let calledExpression = callingExpr. calledExpression
3219- let ( children , hasCompoundExpression, hasMemberAccess) =
3243+ let ( hasCompoundExpression, hasMemberAccess) =
32203244 insertContextualBreaks ( calledExpression, isTopLevel: false )
32213245
32223246 let shouldGroup =
@@ -3241,7 +3265,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
32413265 before ( expr. firstToken, tokens: beforeTokens)
32423266 after ( expr. lastToken, tokens: afterTokens)
32433267 }
3244- return ( [ expr ] + children , true , hasMemberAccess)
3268+ return ( true , hasMemberAccess)
32453269 }
32463270
32473271 // Otherwise, it's an expression that isn't calling another expression (e.g. array or
@@ -3250,7 +3274,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
32503274 before ( expr. firstToken, tokens: . contextualBreakingStart)
32513275 after ( expr. lastToken, tokens: . contextualBreakingEnd)
32523276 let hasCompoundExpression = !expr. is ( IdentifierExprSyntax . self)
3253- return ( [ expr ] , hasCompoundExpression, false )
3277+ return ( hasCompoundExpression, false )
32543278 }
32553279}
32563280
0 commit comments