Skip to content

Commit d6b9cd2

Browse files
committed
feat: support fixit
1 parent 7572558 commit d6b9cd2

File tree

3 files changed

+48
-16
lines changed

3 files changed

+48
-16
lines changed

Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -728,15 +728,25 @@ private class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
728728
}) {
729729
self.expandedAttributes.remove(at: index)
730730
} else {
731-
if let macroRole = try? inferAttachedMacroRole(definition: spec.type), isInvalidAttachedMacro(macroRole: macroRole, attachedTo: declSyntax) {
732-
contextGenerator(node).addDiagnostics(
733-
from: MacroApplicationError.macroAttachedToInvalidDecl(
734-
macroRole.rawValue,
735-
declSyntax.nodeTypeNameForDiagnostics(allowBlockNames: true) ?? ""
736-
),
737-
node: declSyntax
738-
)
739-
}
731+
if let macroRole = try? inferAttachedMacroRole(definition: spec.type),
732+
isInvalidAttachedMacro(macroRole: macroRole, attachedTo: declSyntax)
733+
{
734+
contextGenerator(node).addDiagnostics(
735+
from: MacroApplicationError.macroAttachedToInvalidDecl(
736+
macroRole.rawValue,
737+
declSyntax.nodeTypeNameForDiagnostics(allowBlockNames: true) ?? ""
738+
),
739+
node: declSyntax,
740+
fixIts: [
741+
FixIt(
742+
message: SwiftSyntaxMacros.MacroExpansionFixItMessage(
743+
"Remove '\(attribute.trimmedDescription)'"
744+
),
745+
changes: [.replace(oldNode: Syntax(attribute), newNode: Syntax(AttributeListSyntax()))]
746+
)
747+
]
748+
)
749+
}
740750
}
741751
}
742752
return AttributeRemover(removingWhere: { attributesToRemove.map(\.attributeNode).contains($0) }).rewrite(

Sources/SwiftSyntaxMacros/MacroExpansionContext.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,21 +108,28 @@ private struct ThrownErrorDiagnostic: DiagnosticMessage {
108108

109109
extension MacroExpansionContext {
110110
/// Adds diagnostics from the error thrown during a macro expansion.
111-
public func addDiagnostics(from error: Error, node: some SyntaxProtocol) {
111+
public func addDiagnostics(from error: Error, node: some SyntaxProtocol, fixIts: [FixIt] = []) {
112112
// Inspect the error to form an appropriate set of diagnostics.
113113
var diagnostics: [Diagnostic]
114114
if let diagnosticsError = error as? DiagnosticsError {
115115
diagnostics = diagnosticsError.diagnostics
116116
} else if let message = error as? DiagnosticMessage {
117-
diagnostics = [Diagnostic(node: Syntax(node), message: message)]
117+
diagnostics = [Diagnostic(node: Syntax(node), message: message, fixIts: fixIts)]
118118
} else if let error = error as? SyntaxStringInterpolationInvalidNodeTypeError {
119119
let diagnostic = Diagnostic(
120120
node: Syntax(node),
121-
message: ThrownErrorDiagnostic(message: "Internal macro error: \(error.description)")
121+
message: ThrownErrorDiagnostic(message: "Internal macro error: \(error.description)"),
122+
fixIts: fixIts
122123
)
123124
diagnostics = [diagnostic]
124125
} else {
125-
diagnostics = [Diagnostic(node: Syntax(node), message: ThrownErrorDiagnostic(message: String(describing: error)))]
126+
diagnostics = [
127+
Diagnostic(
128+
node: Syntax(node),
129+
message: ThrownErrorDiagnostic(message: String(describing: error)),
130+
fixIts: fixIts
131+
)
132+
]
126133
}
127134

128135
// Emit the diagnostics.

Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,12 @@ final class AccessorMacroTests: XCTestCase {
427427
"@Test struct Foo {}",
428428
expandedSource: "struct Foo {}",
429429
diagnostics: [
430-
DiagnosticSpec(message: "'accessor' macro cannot be attached to struct", line: 1, column: 1)
430+
DiagnosticSpec(
431+
message: "'accessor' macro cannot be attached to struct",
432+
line: 1,
433+
column: 1,
434+
fixIts: [FixItSpec(message: "Remove '@Test'")]
435+
)
431436
],
432437
macros: ["Test": TestMacro.self]
433438
)
@@ -437,7 +442,12 @@ final class AccessorMacroTests: XCTestCase {
437442
expandedSource: "func Foo() {}",
438443
diagnostics: [
439444
// The compiler will reject this with "'accessor' macro cannot be attached to global function"
440-
DiagnosticSpec(message: "'accessor' macro cannot be attached to function", line: 1, column: 1)
445+
DiagnosticSpec(
446+
message: "'accessor' macro cannot be attached to function",
447+
line: 1,
448+
column: 1,
449+
fixIts: [FixItSpec(message: "Remove '@Test'")]
450+
)
441451
],
442452
macros: ["Test": TestMacro.self]
443453
)
@@ -456,7 +466,12 @@ final class AccessorMacroTests: XCTestCase {
456466
""",
457467
diagnostics: [
458468
// The compiler will reject this with "'accessor' macro cannot be attached to instance method"
459-
DiagnosticSpec(message: "'accessor' macro cannot be attached to function", line: 2, column: 3)
469+
DiagnosticSpec(
470+
message: "'accessor' macro cannot be attached to function",
471+
line: 2,
472+
column: 3,
473+
fixIts: [FixItSpec(message: "Remove '@Test'")]
474+
)
460475
],
461476
macros: ["Test": TestMacro.self],
462477
indentationWidth: indentationWidth

0 commit comments

Comments
 (0)