diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ccfbf026e22d2..0dd850dbc8820 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -9012,5 +9012,7 @@ GROUPED_WARNING(perf_hint_typealias_uses_existential,ExistentialType,none, ERROR(unsafe_self_dependent_result_attr_on_invalid_decl,none, "invalid use of @_unsafeSelfDependentResult", ()) +REMARK(macro_expansion_line, none, "macro content: |%0|", (StringRef)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index be253e3eb0ff6..5a45721ddc51f 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -651,6 +651,9 @@ namespace swift { /// Enables dumping macro expansions. bool DumpMacroExpansions = false; + /// Emits a remark with the content of each macro expansion line, for matching with -verify + bool RemarkMacroExpansions = false; + /// Enables dumping imports for each SourceFile. bool DumpSourceFileImports = false; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 25d477379cb22..5ce9dc392e1b1 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -185,6 +185,9 @@ def verify_generic_signatures : Separate<["-"], "verify-generic-signatures">, MetaVarName<"">, HelpText<"Verify the generic signatures in the given module">; +def expansion_remarks: Flag<["-"], "Rmacro-expansions">, + HelpText<"Show remarks for each line in macro expansions">; + def show_diagnostics_after_fatal : Flag<["-"], "show-diagnostics-after-fatal">, HelpText<"Keep emitting subsequent diagnostics after a fatal error">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index c36efb3235170..111a9c5560f64 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1774,6 +1774,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.DumpMacroExpansions = Args.hasArg( OPT_dump_macro_expansions); + Opts.RemarkMacroExpansions = Args.hasArg( + OPT_expansion_remarks); + Opts.DumpSourceFileImports = Args.hasArg( OPT_dump_source_file_imports); diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 9e57b5a012a13..be392fdc046e6 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -1006,6 +1006,21 @@ static CharSourceRange getExpansionInsertionRange(MacroRole role, llvm_unreachable("unhandled MacroRole"); } +static void remarkMacroExpansionsEmitDiags(ASTContext &ctx, unsigned macroBufferID) { + SourceManager &sourceMgr = ctx.SourceMgr; + CharSourceRange range = sourceMgr.getRangeForBuffer(macroBufferID); + SourceLoc start = range.getStart(); + StringRef content(start.getPointer(), range.getByteLength()); + size_t newline; + do { + newline = content.find('\n'); + StringRef line = content.take_front(newline); + content = content.drop_front(newline + 1); + + ctx.Diags.diagnose(SourceLoc::getFromPointer(line.begin()), diag::macro_expansion_line, line); + } while (newline != StringRef::npos); +} + static SourceFile * createMacroSourceFile(std::unique_ptr buffer, MacroRole role, ASTNode target, DeclContext *dc, @@ -1069,6 +1084,10 @@ createMacroSourceFile(std::unique_ptr buffer, originModule = cast(target)->getModuleContextForNameLookup(); performImportResolutionForClangMacroBuffer(*macroSourceFile, originModule); } + + if (ctx.LangOpts.RemarkMacroExpansions) + remarkMacroExpansionsEmitDiags(ctx, macroBufferID); + return macroSourceFile; } diff --git a/test/Frontend/DiagnosticVerifier/Inputs/macro/unstringify-macro.swift b/test/Frontend/DiagnosticVerifier/Inputs/macro/unstringify-macro.swift new file mode 100644 index 0000000000000..e5c33f86c43d2 --- /dev/null +++ b/test/Frontend/DiagnosticVerifier/Inputs/macro/unstringify-macro.swift @@ -0,0 +1,19 @@ +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct UnstringifyPeerMacro: PeerMacro { + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + do { + let argumentList = node.arguments!.as(LabeledExprListSyntax.self)! + let arguments = [LabeledExprSyntax](argumentList) + let arg = arguments.first!.expression.as(StringLiteralExprSyntax.self)! + let content = arg.representedLiteralValue! + return [DeclSyntax("\(raw: content)")] + } + } +} diff --git a/test/Frontend/DiagnosticVerifier/expansion-remarks.swift b/test/Frontend/DiagnosticVerifier/expansion-remarks.swift new file mode 100644 index 0000000000000..533b729aa4625 --- /dev/null +++ b/test/Frontend/DiagnosticVerifier/expansion-remarks.swift @@ -0,0 +1,36 @@ +// REQUIRES: swift_swift_parser, executable_test + +// RUN: %empty-directory(%t) +// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(UnstringifyMacroDefinition) -module-name=UnstringifyMacroDefinition %S/Inputs/macro/unstringify-macro.swift -g -no-toolchain-stdlib-rpath + +// RUN: %target-typecheck-verify-swift -swift-version 5 -load-plugin-library %t/%target-library-name(UnstringifyMacroDefinition) -Rmacro-expansions + +@attached(peer, names: overloaded) +macro unstringifyPeer(_ s: String) = + #externalMacro(module: "UnstringifyMacroDefinition", type: "UnstringifyPeerMacro") + +// expected-note@+1 *{{in expansion of macro 'unstringifyPeer' on global function 'foo()' here}} +@unstringifyPeer("func foo(_ x: Int) {\nlet a = 2\nlet b = x\n}") +func foo() {} +/* +expected-expansion@-2:14{{ + expected-remark@1{{macro content: |func foo(_ x: Int) {|}} + expected-remark@2{{macro content: | let a = 2|}} + expected-warning@2{{initialization of immutable value 'a' was never used; consider replacing with assignment to '_' or removing it}} + expected-remark@3{{macro content: | let b = x|}} + expected-warning@3{{initialization of immutable value 'b' was never used; consider replacing with assignment to '_' or removing it}} + expected-remark@4{{macro content: |}|}} +}} +*/ + +@freestanding(expression) public macro ExprMacro() -> String = #file + +func bar() { + // expected-note@+1{{in expansion of macro 'ExprMacro' here}} + let _ = #ExprMacro() + /* + expected-expansion@-2:13{{ + expected-remark@1{{macro content: |#file|}} + }} + */ +}