Skip to content

Commit cfe4eb8

Browse files
committed
Add fixed source to assertMacroExpansion
1 parent 35edf53 commit cfe4eb8

File tree

3 files changed

+83
-52
lines changed

3 files changed

+83
-52
lines changed

Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,18 @@ func assertDiagnostic(
254254
/// - macros: The macros that should be expanded, provided as a dictionary
255255
/// mapping macro names (e.g., `"stringify"`) to implementation types
256256
/// (e.g., `StringifyMacro.self`).
257+
/// - applyFixIts: If specified, filters the Fix-Its that are applied to generate `fixedSource` to only those whose message occurs in this array. If `nil`, all Fix-Its from the diagnostics are applied.
258+
/// - fixedSource: If specified, asserts that the source code after applying Fix-Its matches this string.
257259
/// - testModuleName: The name of the test module to use.
258260
/// - testFileName: The name of the test file name to use.
261+
/// - indentationWidth: The indentation width used in the expansion.
259262
public func assertMacroExpansion(
260263
_ originalSource: String,
261264
expandedSource expectedExpandedSource: String,
262265
diagnostics: [DiagnosticSpec] = [],
263266
macros: [String: Macro.Type],
267+
applyFixIts: [String]? = nil,
268+
fixedSource expectedFixedSource: String? = nil,
264269
testModuleName: String = "TestModule",
265270
testFileName: String = "test.swift",
266271
indentationWidth: Trivia = .spaces(4),
@@ -316,4 +321,16 @@ public func assertMacroExpansion(
316321
assertDiagnostic(actualDiag, in: context, expected: expectedDiag)
317322
}
318323
}
324+
325+
// Applying Fix-Its
326+
if let expectedFixedSource = expectedFixedSource {
327+
let fixedTree = FixItApplier.applyFixes(in: diags, withMessages: applyFixIts, to: expandedSourceFile)
328+
let fixedTreeDescription = fixedTree.description
329+
assertStringsEqualWithDiff(
330+
fixedTreeDescription.trimmingTrailingWhitespace(),
331+
expectedFixedSource.trimmingTrailingWhitespace(),
332+
file: file,
333+
line: line
334+
)
335+
}
319336
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftSyntax
14+
import SwiftDiagnostics
15+
16+
public class FixItApplier: SyntaxRewriter {
17+
var changes: [FixIt.Change]
18+
19+
init(diagnostics: [Diagnostic], withMessages messages: [String]?) {
20+
let messages = messages ?? diagnostics.compactMap { $0.fixIts.first?.message.message }
21+
22+
self.changes =
23+
diagnostics
24+
.flatMap { $0.fixIts }
25+
.filter {
26+
return messages.contains($0.message.message)
27+
}
28+
.flatMap { $0.changes }
29+
30+
super.init(viewMode: .all)
31+
}
32+
33+
public override func visitAny(_ node: Syntax) -> Syntax? {
34+
for change in changes {
35+
switch change {
36+
case .replace(oldNode: let oldNode, newNode: let newNode) where oldNode.id == node.id:
37+
return newNode
38+
default:
39+
break
40+
}
41+
}
42+
return nil
43+
}
44+
45+
public override func visit(_ node: TokenSyntax) -> TokenSyntax {
46+
var modifiedNode = node
47+
for change in changes {
48+
switch change {
49+
case .replaceLeadingTrivia(token: let changedNode, newTrivia: let newTrivia) where changedNode.id == node.id:
50+
modifiedNode = node.with(\.leadingTrivia, newTrivia)
51+
case .replaceTrailingTrivia(token: let changedNode, newTrivia: let newTrivia) where changedNode.id == node.id:
52+
modifiedNode = node.with(\.trailingTrivia, newTrivia)
53+
default:
54+
break
55+
}
56+
}
57+
return modifiedNode
58+
}
59+
60+
/// If `messages` is `nil`, applies all Fix-Its in `diagnostics` to `tree` and returns the fixed syntax tree.
61+
/// If `messages` is not `nil`, applies only Fix-Its whose message is in `messages`.
62+
public static func applyFixes<T: SyntaxProtocol>(in diagnostics: [Diagnostic], withMessages messages: [String]?, to tree: T) -> Syntax {
63+
let applier = FixItApplier(diagnostics: diagnostics, withMessages: messages)
64+
return applier.rewrite(tree)
65+
}
66+
}

Tests/SwiftParserTest/Assertions.swift

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -276,58 +276,6 @@ struct DiagnosticSpec {
276276
}
277277
}
278278

279-
class FixItApplier: SyntaxRewriter {
280-
var changes: [FixIt.Change]
281-
282-
init(diagnostics: [Diagnostic], withMessages messages: [String]?) {
283-
let messages = messages ?? diagnostics.compactMap { $0.fixIts.first?.message.message }
284-
285-
self.changes =
286-
diagnostics
287-
.flatMap { $0.fixIts }
288-
.filter {
289-
return messages.contains($0.message.message)
290-
}
291-
.flatMap { $0.changes }
292-
293-
super.init(viewMode: .all)
294-
}
295-
296-
public override func visitAny(_ node: Syntax) -> Syntax? {
297-
for change in changes {
298-
switch change {
299-
case .replace(oldNode: let oldNode, newNode: let newNode) where oldNode.id == node.id:
300-
return newNode
301-
default:
302-
break
303-
}
304-
}
305-
return nil
306-
}
307-
308-
override func visit(_ node: TokenSyntax) -> TokenSyntax {
309-
var modifiedNode = node
310-
for change in changes {
311-
switch change {
312-
case .replaceLeadingTrivia(token: let changedNode, newTrivia: let newTrivia) where changedNode.id == node.id:
313-
modifiedNode = node.with(\.leadingTrivia, newTrivia)
314-
case .replaceTrailingTrivia(token: let changedNode, newTrivia: let newTrivia) where changedNode.id == node.id:
315-
modifiedNode = node.with(\.trailingTrivia, newTrivia)
316-
default:
317-
break
318-
}
319-
}
320-
return modifiedNode
321-
}
322-
323-
/// If `messages` is `nil`, applies all Fix-Its in `diagnostics` to `tree` and returns the fixed syntax tree.
324-
/// If `messages` is not `nil`, applies only Fix-Its whose message is in `messages`.
325-
public static func applyFixes<T: SyntaxProtocol>(in diagnostics: [Diagnostic], withMessages messages: [String]?, to tree: T) -> Syntax {
326-
let applier = FixItApplier(diagnostics: diagnostics, withMessages: messages)
327-
return applier.rewrite(tree)
328-
}
329-
}
330-
331279
/// Assert that `location` is the same as that of `locationMarker` in `tree`.
332280
func assertLocation<T: SyntaxProtocol>(
333281
_ location: SourceLocation,

0 commit comments

Comments
 (0)