Skip to content

Commit 35ff56c

Browse files
authored
Merge pull request #1318 from elizablock/DiagnosticProviding
[Macros] generate diagnostics from thrown error
2 parents 7f83979 + 8c7177e commit 35ff56c

File tree

4 files changed

+57
-54
lines changed

4 files changed

+57
-54
lines changed

Sources/SwiftDiagnostics/Diagnostic.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,9 @@ public struct Diagnostic: CustomDebugStringConvertible {
7171
return "\(location): \(message)"
7272
}
7373
}
74+
75+
public protocol DiagnosticsProviding: Error {
76+
/// The diagnostics provided by this error.
77+
/// At least one diagnostic should have `severity == .error`.
78+
var diagnostics: [Diagnostic] { get }
79+
}

Sources/SwiftSyntaxMacros/MacroExpansionContext.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,51 @@ extension MacroExpansionContext {
6565
}
6666
}
6767

68+
/// Diagnostic message used for thrown errors.
69+
private struct ThrownErrorDiagnostic: DiagnosticMessage {
70+
let message: String
71+
72+
var severity: DiagnosticSeverity { .error }
73+
74+
var diagnosticID: MessageID {
75+
.init(domain: "SwiftSyntaxMacros", id: "ThrownErrorDiagnostic")
76+
}
77+
}
78+
79+
extension MacroExpansionContext {
80+
/// Add diagnostics from the error thrown during macro expansion.
81+
public func addDiagnostics<S: SyntaxProtocol>(from error: Error, node: S) {
82+
guard let diagnosticsProvider = error as? DiagnosticsProviding else {
83+
diagnose(
84+
Diagnostic(
85+
node: Syntax(node),
86+
message: ThrownErrorDiagnostic(message: String(describing: error))
87+
)
88+
)
89+
return
90+
}
91+
92+
let providedDiagnostics = diagnosticsProvider.diagnostics
93+
for diagnostic in providedDiagnostics {
94+
diagnose(diagnostic)
95+
}
96+
97+
// handle possibility that none of the diagnostics was an error
98+
if !providedDiagnostics.contains(
99+
where: { $0.diagMessage.severity == .error }
100+
) {
101+
diagnose(
102+
Diagnostic(
103+
node: Syntax(node),
104+
message: ThrownErrorDiagnostic(
105+
message: "macro expansion failed without generating an error"
106+
)
107+
)
108+
)
109+
}
110+
}
111+
}
112+
68113
/// Describe the position within a syntax node that can be used to compute
69114
/// source locations.
70115
public enum PositionInSyntaxNode {

Sources/SwiftSyntaxMacros/MacroSystem.swift

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,7 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
150150
newItems.append(CodeBlockItemSyntax(item: .init(expandedExpr)))
151151
}
152152
} catch {
153-
// Record the error
154-
context.diagnose(
155-
Diagnostic(
156-
node: Syntax(node),
157-
message: ThrownErrorDiagnostic(message: String(describing: error))
158-
)
159-
)
153+
context.addDiagnostics(from: error, node: node)
160154
}
161155

162156
continue
@@ -200,13 +194,7 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
200194
}
201195
)
202196
} catch {
203-
// Record the error
204-
context.diagnose(
205-
Diagnostic(
206-
node: Syntax(node),
207-
message: ThrownErrorDiagnostic(message: String(describing: error))
208-
)
209-
)
197+
context.addDiagnostics(from: error, node: node)
210198
}
211199

212200
continue
@@ -376,13 +364,7 @@ extension MacroApplication {
376364
let newPeers = try peerMacro.expansion(of: attribute, providingPeersOf: decl, in: context)
377365
peers.append(contentsOf: newPeers)
378366
} catch {
379-
// Record the error
380-
context.diagnose(
381-
Diagnostic(
382-
node: Syntax(attribute),
383-
message: ThrownErrorDiagnostic(message: String(describing: error))
384-
)
385-
)
367+
context.addDiagnostics(from: error, node: attribute)
386368
}
387369
}
388370

@@ -406,13 +388,7 @@ extension MacroApplication {
406388
)
407389
)
408390
} catch {
409-
// Record the error
410-
context.diagnose(
411-
Diagnostic(
412-
node: Syntax(attribute),
413-
message: ThrownErrorDiagnostic(message: String(describing: error))
414-
)
415-
)
391+
context.addDiagnostics(from: error, node: attribute)
416392
}
417393
}
418394

@@ -473,13 +449,7 @@ extension MacroApplication {
473449
contentsOf: try _openExistential(typedDecl, do: expand)
474450
)
475451
} catch {
476-
// Record the error
477-
context.diagnose(
478-
Diagnostic(
479-
node: Syntax(attribute),
480-
message: ThrownErrorDiagnostic(message: String(describing: error))
481-
)
482-
)
452+
context.addDiagnostics(from: error, node: attribute)
483453
}
484454
}
485455

Sources/SwiftSyntaxMacros/Syntax+MacroEvaluation.swift

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,6 @@
1313
import SwiftDiagnostics
1414
import SwiftSyntax
1515

16-
/// Diagnostic message used for thrown errors.
17-
struct ThrownErrorDiagnostic: DiagnosticMessage {
18-
let message: String
19-
20-
var severity: DiagnosticSeverity { .error }
21-
22-
var diagnosticID: MessageID {
23-
.init(domain: "SwiftSyntaxMacros", id: "ThrownErrorDiagnostic")
24-
}
25-
}
26-
2716
extension SyntaxProtocol {
2817
/// Detach the current node and inform the macro expansion context,
2918
/// if it needs to know.
@@ -51,14 +40,7 @@ extension MacroExpansionExprSyntax {
5140
do {
5241
return try exprMacro.expansion(of: detach(in: context), in: context)
5342
} catch {
54-
// Record the error
55-
context.diagnose(
56-
Diagnostic(
57-
node: Syntax(self),
58-
message: ThrownErrorDiagnostic(message: String(describing: error))
59-
)
60-
)
61-
43+
context.addDiagnostics(from: error, node: self)
6244
return ExprSyntax(self)
6345
}
6446
}

0 commit comments

Comments
 (0)