@@ -112,7 +112,7 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
112
112
113
113
let formatter = formatType. init ( context: context)
114
114
let actual = formatter. visit ( sourceFileSyntax)
115
- assertStringsEqualWithDiff ( actual. description , expected, file: file, line: line)
115
+ assertStringsEqualWithDiff ( " \( actual) " , expected, file: file, line: line)
116
116
117
117
assertFindings (
118
118
expected: findings,
@@ -123,15 +123,22 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
123
123
line: line)
124
124
125
125
// Verify that the pretty printer can consume the transformed tree (e.g., it does not contain
126
- // any unfolded `SequenceExpr`s). We don't need to check the actual output here (we don't want
127
- // the rule tests to be pretty-printer dependent), but this will catch invariants that aren't
128
- // satisfied.
129
- _ = PrettyPrinter (
126
+ // any unfolded `SequenceExpr`s). Then do a whitespace-insensitive comparison of the two trees
127
+ // to verify that the format rule didn't transform the tree in such a way that it caused the
128
+ // pretty-printer to drop important information (the most likely case is a format rule
129
+ // misplacing trivia in a way that the pretty-printer isn't able to handle).
130
+ let prettyPrintedSource = PrettyPrinter (
130
131
context: context,
131
132
node: Syntax ( actual) ,
132
133
printTokenStream: false ,
133
134
whitespaceOnly: false
134
135
) . prettyPrint ( )
136
+ let prettyPrintedTree = Parser . parse ( source: prettyPrintedSource)
137
+ XCTAssertEqual (
138
+ whitespaceInsensitiveText ( of: actual) ,
139
+ whitespaceInsensitiveText ( of: prettyPrintedTree) ,
140
+ " After pretty-printing and removing fluid whitespace, the files did not match " ,
141
+ file: file, line: line)
135
142
136
143
var emittedPipelineFindings = [ Finding] ( )
137
144
// Disable default rules, so only select rule runs in pipeline
@@ -149,3 +156,34 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
149
156
emittedFindings: emittedPipelineFindings, context: context, file: file, line: line)
150
157
}
151
158
}
159
+
160
+ /// Returns a string containing a whitespace-insensitive representation of the given source file.
161
+ private func whitespaceInsensitiveText( of file: SourceFileSyntax ) -> String {
162
+ var result = " "
163
+ for token in file. tokens ( viewMode: . sourceAccurate) {
164
+ appendNonspaceTrivia ( token. leadingTrivia, to: & result)
165
+ result. append ( token. text)
166
+ appendNonspaceTrivia ( token. trailingTrivia, to: & result)
167
+ }
168
+ return result
169
+ }
170
+
171
+ /// Appends any non-whitespace trivia pieces from the given trivia collection to the output string.
172
+ private func appendNonspaceTrivia( _ trivia: Trivia , to string: inout String ) {
173
+ for piece in trivia {
174
+ switch piece {
175
+ case . carriageReturnLineFeeds, . carriageReturns, . formfeeds, . newlines, . spaces, . tabs:
176
+ break
177
+ case . lineComment( let comment) , . docLineComment( let comment) :
178
+ // A tree transforming rule might leave whitespace at the end of a line comment, which the
179
+ // pretty printer will remove, so we should ignore that.
180
+ if let lastNonWhitespaceIndex = comment. lastIndex ( where: { !$0. isWhitespace } ) {
181
+ string. append ( contentsOf: comment [ ... lastNonWhitespaceIndex] )
182
+ } else {
183
+ string. append ( comment)
184
+ }
185
+ default :
186
+ piece. write ( to: & string)
187
+ }
188
+ }
189
+ }
0 commit comments