[macros] add -Rmacro-expansions

This adds the -Rmacro-expansions flag. It provides similar functionality
to -dump-macro-expansions, but instead of dumping the macro expansion to
stderr, it emits it line by line as remarks. This is useful for testing
with -verify, where both macro expansion content and warnings need to be
tested at the same time.
This commit is contained in:
Henrik G. Olsson
2025-11-04 17:40:40 -08:00
parent b1c2bc1731
commit 040c9e6729
7 changed files with 85 additions and 0 deletions

View File

@@ -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"

View File

@@ -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;

View File

@@ -185,6 +185,9 @@ def verify_generic_signatures : Separate<["-"], "verify-generic-signatures">,
MetaVarName<"<module-name>">,
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">;

View File

@@ -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);

View File

@@ -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<llvm::MemoryBuffer> buffer,
MacroRole role, ASTNode target, DeclContext *dc,
@@ -1069,6 +1084,10 @@ createMacroSourceFile(std::unique_ptr<llvm::MemoryBuffer> buffer,
originModule = cast<Decl *>(target)->getModuleContextForNameLookup();
performImportResolutionForClangMacroBuffer(*macroSourceFile, originModule);
}
if (ctx.LangOpts.RemarkMacroExpansions)
remarkMacroExpansionsEmitDiags(ctx, macroBufferID);
return macroSourceFile;
}

View File

@@ -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)")]
}
}
}

View File

@@ -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|}}
}}
*/
}