Skip to content

[Macros] Unpack DiagnosticsError into separate diagnostics #64744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions lib/ASTGen/Sources/ASTGen/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,14 @@ func expandFreestandingMacroInProcess(
print("not an expression macro or a declaration macro")
return nil
}
} catch let diagsError as DiagnosticsError {
for diag in diagsError.diagnostics {
sourceManager.diagnose(
diagnostic: diag,
messageSuffix: " (from macro '\(macroName)')"
)
}
return nil
} catch {
// Record the error
sourceManager.diagnose(
Expand Down Expand Up @@ -805,6 +813,15 @@ func expandAttachedMacroInProcess(
print("\(macroPtr) does not conform to any known attached macro protocol")
return nil
}
} catch let diagsError as DiagnosticsError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just call addDiagnostics in the block below instead, which would better match things on the swift-syntax side. Alternatively we could have a non-throwing extension that does this on the swift-syntax side as well.

That would also avoid repeating the from macro bits which is ... kind of nice 🤷

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right, I'm doing this in #64785

for diag in diagsError.diagnostics {
sourceManager.diagnose(
diagnostic: diag,
messageSuffix: " (from macro '\(macroName)')"
)
}

return nil
} catch {
// Record the error
// FIXME: Need to decide where to diagnose the error:
Expand Down
45 changes: 45 additions & 0 deletions test/Macros/Inputs/syntax_macro_definitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,51 @@ public struct WarningMacro: ExpressionMacro {
}
}

public struct ErrorMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
guard let firstElement = macro.argumentList.first,
let stringLiteral = firstElement.expression.as(StringLiteralExprSyntax.self),
stringLiteral.segments.count == 1,
case let .stringSegment(messageString)? = stringLiteral.segments.first
else {
let errorNode: Syntax
if let firstElement = macro.argumentList.first {
errorNode = Syntax(firstElement)
} else {
errorNode = Syntax(macro)
}

let messageID = MessageID(domain: "silly", id: "error")
let diag = Diagnostic(
node: errorNode,
message: SimpleDiagnosticMessage(
message: "#myError macro requires a string literal",
diagnosticID: messageID,
severity: .error
)
)

throw DiagnosticsError(diagnostics: [diag])
}

context.diagnose(
Diagnostic(
node: Syntax(macro),
message: SimpleDiagnosticMessage(
message: messageString.content.description,
diagnosticID: MessageID(domain: "test", id: "error"),
severity: .error
)
)
)

return "()"
}
}

public struct PropertyWrapperMacro {}

extension PropertyWrapperMacro: AccessorMacro, Macro {
Expand Down
15 changes: 14 additions & 1 deletion test/Macros/macro_expand_throwing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// RUN: %target-build-swift -swift-version 5 -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

// Make sure the diagnostic comes through...
// RUN: %target-typecheck-verify-swift -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir -module-name MacroUser
// RUN: %target-typecheck-verify-swift -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir -module-name MacroUser -DTEST_DIAGNOSTICS

// Make sure the diagnostic doesn't crash in SILGen
// RUN: not %target-swift-frontend -swift-version 5 -emit-sil -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir %s -module-name MacroUser -o - -g
Expand All @@ -12,9 +12,22 @@

@freestanding(expression) macro myWarning(_ message: String) = #externalMacro(module: "MacroDefinition", type: "WarningMacro")

@freestanding(expression) macro myError(_ message: String) = #externalMacro(module: "MacroDefinition", type: "ErrorMacro")

func testThrownError() {
let name = "hello"
#myWarning (name) // expected-error{{#myWarning macro requires a string literal (from macro 'myWarning')}}

#myWarning("experimental features ahead") // expected-warning{{experimental features ahead}}
}

#if TEST_DIAGNOSTICS
func testThrownErrors() {
let name = "hello"
#myError(
name
) // expected-error@-1{{macro requires a string literal (from macro 'myError')}}

#myError("experimental features ahead") // expected-error{{experimental features ahead}}
}
#endif