Skip to content

Commit 63f4001

Browse files
authored
Merge pull request #434 from allevato/legacy-trivia-workaround
Workaround new trivia behavior in SwiftSyntax.
2 parents fd10c65 + c5c4dac commit 63f4001

File tree

6 files changed

+67
-12
lines changed

6 files changed

+67
-12
lines changed

Sources/SwiftFormat/Parsing.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import Foundation
1414
import SwiftDiagnostics
15+
import SwiftFormatCore
1516
import SwiftOperators
1617
import SwiftParser
1718
import SwiftSyntax
@@ -54,5 +55,5 @@ func parseAndEmitDiagnostics(
5455
throw SwiftFormatError.fileContainsInvalidSyntax
5556
}
5657

57-
return sourceFile
58+
return restoringLegacyTriviaBehavior(sourceFile)
5859
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import SwiftSyntax
2+
3+
/// Rewrites the trivia on tokens in the given source file to restore the legacy trivia behavior
4+
/// before https://github.com/apple/swift-syntax/pull/985 was merged.
5+
///
6+
/// Eventually we should get rid of this and update the core formatting code to adjust to the new
7+
/// behavior, but this workaround lets us keep the current implementation without larger changes.
8+
public func restoringLegacyTriviaBehavior(_ sourceFile: SourceFileSyntax) -> SourceFileSyntax {
9+
return LegacyTriviaBehaviorRewriter().visit(sourceFile).as(SourceFileSyntax.self)!
10+
}
11+
12+
private final class LegacyTriviaBehaviorRewriter: SyntaxRewriter {
13+
/// Trivia that was extracted from the trailing trivia of a token to be prepended to the leading
14+
/// trivia of the next token.
15+
private var pendingLeadingTrivia: Trivia?
16+
17+
override func visit(_ token: TokenSyntax) -> TokenSyntax {
18+
var token = token
19+
if let pendingLeadingTrivia = pendingLeadingTrivia {
20+
token = token.withLeadingTrivia(pendingLeadingTrivia + token.leadingTrivia)
21+
self.pendingLeadingTrivia = nil
22+
}
23+
if token.nextToken != nil,
24+
let firstIndexToMove = token.trailingTrivia.firstIndex(where: shouldTriviaPieceBeMoved)
25+
{
26+
pendingLeadingTrivia = Trivia(pieces: Array(token.trailingTrivia[firstIndexToMove...]))
27+
token =
28+
token.withTrailingTrivia(Trivia(pieces: Array(token.trailingTrivia[..<firstIndexToMove])))
29+
}
30+
return token
31+
}
32+
}
33+
34+
/// Returns a value indicating whether the given trivia piece should be moved from a token's
35+
/// trailing trivia to the leading trivia of the following token to restore the legacy trivia
36+
/// behavior.
37+
private func shouldTriviaPieceBeMoved(_ piece: TriviaPiece) -> Bool {
38+
switch piece {
39+
case .spaces, .tabs, .unexpectedText:
40+
return false
41+
default:
42+
return true
43+
}
44+
}

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,15 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
12491249
return .visitChildren
12501250
}
12511251

1252+
override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind {
1253+
arrangeFunctionCallArgumentList(
1254+
node.argumentList,
1255+
leftDelimiter: node.leftParen,
1256+
rightDelimiter: node.rightParen,
1257+
forcesBreakBeforeRightDelimiter: false)
1258+
return .visitChildren
1259+
}
1260+
12521261
override func visit(_ node: ParameterClauseSyntax) -> SyntaxVisitorContinueKind {
12531262
// Prioritize keeping ") throws -> <return_type>" together. We can only do this if the function
12541263
// has arguments.
@@ -3595,11 +3604,11 @@ class CommentMovingRewriter: SyntaxRewriter {
35953604
return super.visit(node)
35963605
}
35973606

3598-
override func visit(_ token: TokenSyntax) -> Syntax {
3607+
override func visit(_ token: TokenSyntax) -> TokenSyntax {
35993608
if let rewrittenTrivia = rewriteTokenTriviaMap[token] {
3600-
return Syntax(token.withLeadingTrivia(rewrittenTrivia))
3609+
return token.withLeadingTrivia(rewrittenTrivia)
36013610
}
3602-
return Syntax(token)
3611+
return token
36033612
}
36043613

36053614
override func visit(_ node: InfixOperatorExprSyntax) -> ExprSyntax {

Sources/SwiftFormatRules/ReplaceTrivia.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ fileprivate final class ReplaceTrivia: SyntaxRewriter {
2626
self.trailingTrivia = trailingTrivia
2727
}
2828

29-
override func visit(_ token: TokenSyntax) -> Syntax {
30-
guard token == self.token else { return Syntax(token) }
31-
let newNode = token.withLeadingTrivia(leadingTrivia ?? token.leadingTrivia)
29+
override func visit(_ token: TokenSyntax) -> TokenSyntax {
30+
guard token == self.token else { return token }
31+
return token
32+
.withLeadingTrivia(leadingTrivia ?? token.leadingTrivia)
3233
.withTrailingTrivia(trailingTrivia ?? token.trailingTrivia)
33-
return Syntax(newNode)
3434
}
3535
}
3636

Tests/SwiftFormatPrettyPrintTests/PrettyPrintTestCase.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ class PrettyPrintTestCase: DiagnosingTestCase {
6767
) -> String? {
6868
// Ignore folding errors for unrecognized operators so that we fallback to a reasonable default.
6969
let sourceFileSyntax =
70-
OperatorTable.standardOperators.foldAll(Parser.parse(source: source)) { _ in }
71-
.as(SourceFileSyntax.self)!
70+
restoringLegacyTriviaBehavior(
71+
OperatorTable.standardOperators.foldAll(Parser.parse(source: source)) { _ in }
72+
.as(SourceFileSyntax.self)!)
7273
let context = makeContext(sourceFileSyntax: sourceFileSyntax, configuration: configuration)
7374
let printer = PrettyPrinter(
7475
context: context,

Tests/SwiftFormatRulesTests/LintOrFormatRuleTestCase.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
1919
file: StaticString = #file,
2020
line: UInt = #line
2121
) {
22-
let sourceFileSyntax = Parser.parse(source: input)
22+
let sourceFileSyntax = restoringLegacyTriviaBehavior(Parser.parse(source: input))
2323

2424
// Force the rule to be enabled while we test it.
2525
var configuration = Configuration()
@@ -56,7 +56,7 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
5656
file: StaticString = #file,
5757
line: UInt = #line
5858
) {
59-
let sourceFileSyntax = Parser.parse(source: input)
59+
let sourceFileSyntax = restoringLegacyTriviaBehavior(Parser.parse(source: input))
6060

6161
// Force the rule to be enabled while we test it.
6262
var configuration = configuration ?? Configuration()

0 commit comments

Comments
 (0)