Skip to content

Commit cbdb180

Browse files
committed
[Macros] Fix emission of thrown errors.
Errors that were thrown out of a macro implementation were being emitted and then lost. The result of this is a SILGen assertion later on, if there were no other errors in the code. Make sure we properly emit these diagnostics through the source manager, which required fixing an issue in the offset computation used for diagnostics.
1 parent b778b4b commit cbdb180

File tree

5 files changed

+65
-20
lines changed

5 files changed

+65
-20
lines changed

lib/ASTGen/Sources/ASTGen/Macros.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,11 @@ func expandFreestandingMacro(
199199
print("not on a macro expansion node: \(token.recursiveDescription)")
200200
return -1
201201
}
202+
macroName = parentExpansion.macro.text
202203
let decls = try declMacro.expansion(
203204
of: sourceManager.detach(parentExpansion, in: context),
204205
in: context
205206
)
206-
macroName = parentExpansion.macro.text
207207
evaluatedSyntax = Syntax(CodeBlockItemListSyntax(
208208
decls.map { CodeBlockItemSyntax(item: .decl($0)) }))
209209

@@ -213,11 +213,12 @@ func expandFreestandingMacro(
213213
}
214214
} catch {
215215
// Record the error
216-
context.diagnose(
217-
Diagnostic(
216+
sourceManager.diagnose(
217+
diagnostic: Diagnostic(
218218
node: parentSyntax,
219219
message: ThrownErrorDiagnostic(message: String(describing: error))
220-
)
220+
),
221+
messageSuffix: " (from macro '\(macroName)')"
221222
)
222223
return -1
223224
}
@@ -426,11 +427,12 @@ func expandAttachedMacro(
426427
} catch {
427428
// Record the error
428429
// FIXME: Need to decide where to diagnose the error:
429-
context.diagnose(
430-
Diagnostic(
430+
sourceManager.diagnose(
431+
diagnostic: Diagnostic(
431432
node: Syntax(declarationNode),
432433
message: ThrownErrorDiagnostic(message: String(describing: error))
433-
)
434+
),
435+
messageSuffix: " (from macro '\(macroName)')"
434436
)
435437

436438
return 1

lib/ASTGen/Sources/ASTGen/SourceManager.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,17 @@ extension SourceManager {
7676
return nil
7777
}
7878

79-
// Recursively find the root and its offset. This also must succeed.
79+
// Recursively find the root and its offset.
8080
guard let (rootSF, parentOffset) = rootSourceFile(of: parent) else {
8181
return nil
8282
}
8383

84-
// Add our offset to our parent's offset, and we're done.
85-
return (rootSF, parentOffset + SourceLength(utf8Length: offset))
84+
// The position of our node is...
85+
let finalPosition =
86+
node.position // Our position relative to its root
87+
+ SourceLength(utf8Length: offset) // and that root's offset in its parent
88+
+ SourceLength(utf8Length: parentOffset.utf8Offset)
89+
return (rootSF, finalPosition)
8690
}
8791

8892
/// Produce the C++ source location for a given position based on a
@@ -106,7 +110,7 @@ extension SourceManager {
106110
// node.
107111
let position = position ?? node.position
108112
let offsetWithinSyntaxNode =
109-
position.utf8Offset - node.root.position.utf8Offset
113+
position.utf8Offset - node.position.utf8Offset
110114
let offsetFromSourceFile = rootPosition.utf8Offset + offsetWithinSyntaxNode
111115

112116
// Compute the resulting address.

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,6 @@ public enum AddBlocker: ExpressionMacro {
147147
of node: some FreestandingMacroExpansionSyntax,
148148
in context: some MacroExpansionContext
149149
) -> ExprSyntax {
150-
let opTable = OperatorTable.standardOperators
151-
let node = opTable.foldAll(node) { error in
152-
context.diagnose(error.asDiagnostic)
153-
}.asProtocol(FreestandingMacroExpansionSyntax.self)!
154-
155-
guard let argument = node.argumentList.first?.expression else {
156-
fatalError("boom")
157-
}
158-
159150
let visitor = AddVisitor()
160151
let result = visitor.visit(Syntax(node))
161152

@@ -242,6 +233,35 @@ public struct DefineBitwidthNumberedStructsMacro: DeclarationMacro {
242233
}
243234
}
244235

236+
public struct WarningMacro: ExpressionMacro {
237+
public static func expansion(
238+
of macro: some FreestandingMacroExpansionSyntax,
239+
in context: some MacroExpansionContext
240+
) throws -> ExprSyntax {
241+
guard let firstElement = macro.argumentList.first,
242+
let stringLiteral = firstElement.expression
243+
.as(StringLiteralExprSyntax.self),
244+
stringLiteral.segments.count == 1,
245+
case let .stringSegment(messageString)? = stringLiteral.segments.first
246+
else {
247+
throw CustomError.message("#myWarning macro requires a string literal")
248+
}
249+
250+
context.diagnose(
251+
Diagnostic(
252+
node: Syntax(macro),
253+
message: SimpleDiagnosticMessage(
254+
message: messageString.content.description,
255+
diagnosticID: MessageID(domain: "test", id: "error"),
256+
severity: .warning
257+
)
258+
)
259+
)
260+
261+
return "()"
262+
}
263+
}
264+
245265
public struct PropertyWrapperMacro {}
246266

247267
extension PropertyWrapperMacro: AccessorMacro, Macro {

test/Macros/macro_expand.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// Debug info SIL testing
1212
// RUN: %target-swift-frontend -emit-sil -enable-experimental-feature Macros -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir %s -module-name MacroUser -o - -g | %FileCheck --check-prefix CHECK-SIL %s
1313

14+
// RUN: %target-swift-frontend -emit-sil -enable-experimental-feature Macros -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir %s -module-name MacroUser -o - -g | %FileCheck --check-prefix CHECK-SIL %s
15+
1416
// Execution testing
1517
// RUN: %target-build-swift -g -enable-experimental-feature Macros -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir -L %swift-host-lib-dir %s -o %t/main -module-name MacroUser
1618
// RUN: %target-run %t/main | %FileCheck %s
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -I %swift-host-lib-dir -L %swift-host-lib-dir -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath -swift-version 5
3+
4+
// Make sure the diagnostic comes through...
5+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir -module-name MacroUser
6+
7+
// Make sure the diagnostic doesn't crash in SILGen
8+
// RUN: not %target-swift-frontend -emit-sil -enable-experimental-feature Macros -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir %s -module-name MacroUser -o - -g
9+
10+
@freestanding(expression) macro myWarning(_ message: String) = #externalMacro(module: "MacroDefinition", type: "WarningMacro")
11+
12+
func testThrownError() {
13+
let name = "hello"
14+
#myWarning(name) // expected-error{{#myWarning macro requires a string literal (from macro 'myWarning')}}
15+
16+
#myWarning("experimental features ahead") // expected-warning{{experimental features ahead}}
17+
}

0 commit comments

Comments
 (0)