Skip to content

Commit 14a3d23

Browse files
authored
Merge pull request #2115 from ahoppen/ahoppen/multiple-errors
Fix issue that caused errors thrown from macro expansion to show up twice in MacroSystem
2 parents f4fa6e7 + c32dca9 commit 14a3d23

File tree

4 files changed

+166
-17
lines changed

4 files changed

+166
-17
lines changed

Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -485,10 +485,14 @@ private class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
485485
// Note that 'MacroExpansionExpr'/'MacroExpansionExprDecl' at code item
486486
// position are handled by 'visit(_:CodeBlockItemListSyntax)'.
487487
// Only expression expansions inside other syntax nodes is handled here.
488-
if let expanded = expandExpr(node: node) {
488+
switch expandExpr(node: node) {
489+
case .success(let expanded):
489490
return Syntax(visit(expanded))
491+
case .failure:
492+
return Syntax(node)
493+
case .notAMacro:
494+
break
490495
}
491-
492496
if let declSyntax = node.as(DeclSyntax.self),
493497
let attributedNode = node.asProtocol(WithAttributesSyntax.self),
494498
!attributedNode.attributes.isEmpty
@@ -510,16 +514,21 @@ private class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
510514
var newItems: [CodeBlockItemSyntax] = []
511515
func addResult(_ node: CodeBlockItemSyntax) {
512516
// Expand freestanding macro.
513-
if let expanded = expandCodeBlockItem(node: node) {
517+
switch expandCodeBlockItem(node: node) {
518+
case .success(let expanded):
514519
for item in expanded {
515520
addResult(item)
516521
}
517522
return
523+
case .failure:
524+
// Expanding the macro threw an error. We don't have an expanded source.
525+
// Retain the macro node as-is.
526+
newItems.append(node)
527+
case .notAMacro:
528+
// Recurse on the child node
529+
newItems.append(visit(node))
518530
}
519531

520-
// Recurse on the child node
521-
newItems.append(visit(node))
522-
523532
// Expand any peer macro on this item.
524533
if case .decl(let decl) = node.item {
525534
for peer in expandCodeBlockPeers(of: decl) {
@@ -552,16 +561,19 @@ private class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
552561

553562
func addResult(_ node: MemberBlockItemSyntax) {
554563
// Expand freestanding macro.
555-
if let expanded = expandMemberDecl(node: node) {
564+
switch expandMemberDecl(node: node) {
565+
case .success(let expanded):
556566
for item in expanded {
557567
addResult(item)
558568
}
559569
return
570+
case .failure:
571+
newItems.append(node)
572+
case .notAMacro:
573+
// Recurse on the child node.
574+
newItems.append(visit(node))
560575
}
561576

562-
// Recurse on the child node.
563-
newItems.append(visit(node))
564-
565577
// Expand any peer macro on this member.
566578
for peer in expandMemberDeclPeers(of: node.decl) {
567579
addResult(peer)
@@ -846,20 +858,36 @@ extension MacroApplication {
846858
// MARK: Freestanding macro expansion
847859

848860
extension MacroApplication {
861+
enum MacroExpansionResult<ResultType> {
862+
/// Expansion of the macro succeeded.
863+
case success(ResultType)
864+
865+
/// Macro system found the macro to expand but running the expansion threw
866+
/// an error and thus no expansion result exists.
867+
case failure
868+
869+
/// The node that should be expanded was not a macro known to the macro system.
870+
case notAMacro
871+
}
872+
849873
private func expandFreestandingMacro<ExpandedMacroType: SyntaxProtocol>(
850874
_ node: (any FreestandingMacroExpansionSyntax)?,
851875
expandMacro: (_ macro: Macro.Type, _ node: any FreestandingMacroExpansionSyntax) throws -> ExpandedMacroType?
852-
) -> ExpandedMacroType? {
876+
) -> MacroExpansionResult<ExpandedMacroType> {
853877
guard let node,
854878
let macro = macroSystem.lookup(node.macroName.text)
855879
else {
856-
return nil
880+
return .notAMacro
857881
}
858882
do {
859-
return try expandMacro(macro, node)
883+
if let expanded = try expandMacro(macro, node) {
884+
return .success(expanded)
885+
} else {
886+
return .failure
887+
}
860888
} catch {
861889
context.addDiagnostics(from: error, node: node)
862-
return nil
890+
return .failure
863891
}
864892
}
865893

@@ -871,7 +899,7 @@ extension MacroApplication {
871899
/// #foo
872900
/// }
873901
/// ```
874-
func expandCodeBlockItem(node: CodeBlockItemSyntax) -> CodeBlockItemListSyntax? {
902+
func expandCodeBlockItem(node: CodeBlockItemSyntax) -> MacroExpansionResult<CodeBlockItemListSyntax> {
875903
return expandFreestandingMacro(node.item.asProtocol(FreestandingMacroExpansionSyntax.self)) { macro, node in
876904
return try expandFreestandingCodeItemList(
877905
definition: macro,
@@ -890,7 +918,7 @@ extension MacroApplication {
890918
/// #foo
891919
/// }
892920
/// ```
893-
func expandMemberDecl(node: MemberBlockItemSyntax) -> MemberBlockItemListSyntax? {
921+
func expandMemberDecl(node: MemberBlockItemSyntax) -> MacroExpansionResult<MemberBlockItemListSyntax> {
894922
return expandFreestandingMacro(node.decl.as(MacroExpansionDeclSyntax.self)) { macro, node in
895923
return try expandFreestandingMemberDeclList(
896924
definition: macro,
@@ -908,7 +936,7 @@ extension MacroApplication {
908936
/// ```swift
909937
/// let a = #foo
910938
/// ```
911-
func expandExpr(node: Syntax) -> ExprSyntax? {
939+
func expandExpr(node: Syntax) -> MacroExpansionResult<ExprSyntax> {
912940
return expandFreestandingMacro(node.as(MacroExpansionExprSyntax.self)) { macro, node in
913941
return try expandFreestandingExpr(
914942
definition: macro,

Tests/SwiftSyntaxMacroExpansionTest/DeclarationMacroTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,46 @@ final class DeclarationMacroTests: XCTestCase {
347347
indentationWidth: indentationWidth
348348
)
349349
}
350+
351+
func testThrowErrorFromDeclMacro() {
352+
struct MyError: Error, CustomStringConvertible {
353+
let description: String = "my error"
354+
}
355+
356+
struct TestMacro: DeclarationMacro {
357+
static func expansion(
358+
of node: some FreestandingMacroExpansionSyntax,
359+
in context: some MacroExpansionContext
360+
) throws -> [DeclSyntax] {
361+
throw MyError()
362+
}
363+
}
364+
365+
assertMacroExpansion(
366+
"#test",
367+
expandedSource: "#test",
368+
diagnostics: [
369+
DiagnosticSpec(message: "my error", line: 1, column: 1)
370+
],
371+
macros: ["test": TestMacro.self]
372+
)
373+
374+
assertMacroExpansion(
375+
"""
376+
struct Foo {
377+
#test
378+
}
379+
""",
380+
expandedSource: """
381+
struct Foo {
382+
#test
383+
}
384+
""",
385+
diagnostics: [
386+
DiagnosticSpec(message: "my error", line: 2, column: 3)
387+
],
388+
macros: ["test": TestMacro.self]
389+
)
390+
}
391+
350392
}

Tests/SwiftSyntaxMacroExpansionTest/ExpressionMacroTests.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,38 @@ final class ExpressionMacroTests: XCTestCase {
200200
indentationWidth: indentationWidth
201201
)
202202
}
203+
204+
func testThrowErrorFromExpressionMacro() {
205+
struct MyError: Error, CustomStringConvertible {
206+
let description: String = "my error"
207+
}
208+
209+
struct TestMacro: ExpressionMacro {
210+
public static func expansion(
211+
of node: some FreestandingMacroExpansionSyntax,
212+
in context: some MacroExpansionContext
213+
) throws -> ExprSyntax {
214+
throw MyError()
215+
}
216+
}
217+
218+
assertMacroExpansion(
219+
"#test",
220+
expandedSource: "#test",
221+
diagnostics: [
222+
DiagnosticSpec(message: "my error", line: 1, column: 1)
223+
],
224+
macros: ["test": TestMacro.self]
225+
)
226+
227+
assertMacroExpansion(
228+
"1 + #test",
229+
expandedSource: "1 + #test",
230+
diagnostics: [
231+
DiagnosticSpec(message: "my error", line: 1, column: 5)
232+
],
233+
macros: ["test": TestMacro.self]
234+
)
235+
}
236+
203237
}

Tests/SwiftSyntaxMacroExpansionTest/MultiRoleMacroTests.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,51 @@ final class MultiRoleMacroTests: XCTestCase {
204204
macros: ["customTypeWrapper": CustomTypeWrapperMacro.self],
205205
indentationWidth: indentationWidth
206206
)
207+
}
208+
209+
func testAttachedMacroOnFreestandingMacro() {
210+
struct DeclMacro: DeclarationMacro {
211+
static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
212+
return ["var x: Int"]
213+
}
214+
}
215+
216+
struct MyPeerMacro: PeerMacro {
217+
static func expansion(of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws
218+
-> [DeclSyntax]
219+
{
220+
return ["var peer: Int"]
221+
}
222+
}
223+
224+
assertMacroExpansion(
225+
"""
226+
struct Foo {
227+
@Peer
228+
#decl
229+
}
230+
""",
231+
expandedSource: """
232+
struct Foo {
233+
var x: Int
207234
235+
var peer: Int
236+
}
237+
""",
238+
macros: ["decl": DeclMacro.self, "Peer": MyPeerMacro.self]
239+
)
240+
241+
assertMacroExpansion(
242+
"""
243+
@Peer
244+
#decl
245+
""",
246+
expandedSource: """
247+
var x: Int
248+
249+
var peer: Int
250+
""",
251+
macros: ["decl": DeclMacro.self, "Peer": MyPeerMacro.self]
252+
)
208253
}
209254
}

0 commit comments

Comments
 (0)