diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1873,6 +1873,8 @@ PreprocessorOutputOpts<"RewriteIncludes">, DefaultFalse, PosFlag, NegFlag>; +defm directives_only : OptInCC1FFlag<"directives-only", "">; + defm delete_null_pointer_checks : BoolFOption<"delete-null-pointer-checks", CodeGenOpts<"NullPointerIsValid">, DefaultFalse, NegFlag, diff --git a/clang/include/clang/Frontend/PreprocessorOutputOptions.h b/clang/include/clang/Frontend/PreprocessorOutputOptions.h --- a/clang/include/clang/Frontend/PreprocessorOutputOptions.h +++ b/clang/include/clang/Frontend/PreprocessorOutputOptions.h @@ -25,6 +25,7 @@ unsigned RewriteIncludes : 1; ///< Preprocess include directives only. unsigned RewriteImports : 1; ///< Include contents of transitively-imported modules. unsigned MinimizeWhitespace : 1; ///< Ignore whitespace from input. + unsigned DirectivesOnly : 1; ///< Process directives but do not expand macros. public: PreprocessorOutputOptions() { @@ -38,6 +39,7 @@ RewriteIncludes = 0; RewriteImports = 0; MinimizeWhitespace = 0; + DirectivesOnly = 0; } }; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -4413,6 +4413,8 @@ GenerateArg(Args, OPT_dM, SA); if (!Generate_dM && Opts.ShowMacros) GenerateArg(Args, OPT_dD, SA); + if (Opts.DirectivesOnly) + GenerateArg(Args, OPT_fdirectives_only, SA); } static bool ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, @@ -4435,6 +4437,7 @@ Opts.ShowCPP = isStrictlyPreprocessorAction(Action) && !Args.hasArg(OPT_dM); Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD); + Opts.DirectivesOnly = Args.hasArg(OPT_fdirectives_only); return Diags.getNumErrors() == NumErrorsBefore; } diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -96,6 +96,7 @@ bool UseLineDirectives; bool IsFirstFileEntered; bool MinimizeWhitespace; + bool DirectivesOnly; Token PrevTok; Token PrevPrevTok; @@ -103,12 +104,13 @@ public: PrintPPOutputPPCallbacks(Preprocessor &pp, raw_ostream &os, bool lineMarkers, bool defines, bool DumpIncludeDirectives, - bool UseLineDirectives, bool MinimizeWhitespace) + bool UseLineDirectives, bool MinimizeWhitespace, + bool DirectivesOnly) : PP(pp), SM(PP.getSourceManager()), ConcatInfo(PP), OS(os), DisableLineMarkers(lineMarkers), DumpDefines(defines), DumpIncludeDirectives(DumpIncludeDirectives), UseLineDirectives(UseLineDirectives), - MinimizeWhitespace(MinimizeWhitespace) { + MinimizeWhitespace(MinimizeWhitespace), DirectivesOnly(DirectivesOnly) { CurLine = 0; CurFilename += ""; EmittedTokensOnThisLine = false; @@ -467,12 +469,21 @@ void PrintPPOutputPPCallbacks::MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) { const MacroInfo *MI = MD->getMacroInfo(); - // Only print out macro definitions in -dD mode. - if (!DumpDefines || + // Print out macro definitions in -dD mode and when we have -fdirectives-only + // for C++20 header units. + if ((!DumpDefines && !DirectivesOnly) || // Ignore __FILE__ etc. - MI->isBuiltinMacro()) return; + MI->isBuiltinMacro()) + return; - MoveToLine(MI->getDefinitionLoc(), /*RequireStartOfLine=*/true); + SourceLocation DefLoc = MI->getDefinitionLoc(); + if (DirectivesOnly && !MI->isUsed()) { + SourceManager &SM = PP.getSourceManager(); + if (SM.isWrittenInBuiltinFile(DefLoc) || + SM.isWrittenInCommandLineFile(DefLoc)) + return; + } + MoveToLine(DefLoc, /*RequireStartOfLine=*/true); PrintMacroDefinition(*MacroNameTok.getIdentifierInfo(), *MI, PP, OS); setEmittedDirectiveOnThisLine(); } @@ -480,8 +491,10 @@ void PrintPPOutputPPCallbacks::MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, const MacroDirective *Undef) { - // Only print out macro definitions in -dD mode. - if (!DumpDefines) return; + // Print out macro definitions in -dD mode and when we have -fdirectives-only + // for C++20 header units. + if (!DumpDefines && !DirectivesOnly) + return; MoveToLine(MacroNameTok.getLocation(), /*RequireStartOfLine=*/true); OS << "#undef " << MacroNameTok.getIdentifierInfo()->getName(); @@ -959,7 +972,7 @@ PrintPPOutputPPCallbacks *Callbacks = new PrintPPOutputPPCallbacks( PP, *OS, !Opts.ShowLineMarkers, Opts.ShowMacros, Opts.ShowIncludeDirectives, Opts.UseLineDirectives, - Opts.MinimizeWhitespace); + Opts.MinimizeWhitespace, Opts.DirectivesOnly); // Expand macros in pragmas with -fms-extensions. The assumption is that // the majority of pragmas in such a file will be Microsoft pragmas. @@ -995,6 +1008,8 @@ // After we have configured the preprocessor, enter the main file. PP.EnterMainSourceFile(); + if (Opts.DirectivesOnly) + PP.SetMacroExpansionOnlyInDirectives(); // Consume all of the tokens that come from the predefines buffer. Those // should not be emitted into the output and are guaranteed to be at the diff --git a/clang/test/Modules/cxx20-hu-06.cpp b/clang/test/Modules/cxx20-hu-06.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-hu-06.cpp @@ -0,0 +1,68 @@ +// Test check that consuming -E -fdirectives-only output produces the expected +// header unit. + +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// RUN: cd %t + +// RUN: %clang_cc1 -std=c++20 -E -fdirectives-only -xc++-user-header hu-01.h \ +// RUN: -o hu-01.iih + +// RUN: %clang_cc1 -std=c++20 -emit-header-unit \ +// RUN: -xc++-user-header-cpp-output hu-01.iih -o hu-01.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header hu-02.h \ +// RUN: -DFOO -fmodule-file=hu-01.pcm -o hu-02.pcm -Rmodule-import 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-IMP %s -DTDIR=%t + +//--- hu-01.h +#ifndef __GUARD +#define __GUARD + +int baz(int); +#define FORTYTWO 42 + +#define SHOULD_NOT_BE_DEFINED -1 +#undef SHOULD_NOT_BE_DEFINED + +#endif // __GUARD +// expected-no-diagnostics + +//--- hu-02.h +export import "hu-01.h"; +#if !defined(FORTYTWO) || FORTYTWO != 42 +#error FORTYTWO missing in hu-02 +#endif + +#ifndef __GUARD +#error __GUARD missing in hu-02 +#endif + +#ifdef SHOULD_NOT_BE_DEFINED +#error SHOULD_NOT_BE_DEFINED is visible +#endif + +// Make sure that we have not discarded macros from the builtin file. +#ifndef __cplusplus +#error we dropped a defined macro +#endif + +#define KAP 6174 + +#ifdef FOO +#define FOO_BRANCH(X) (X) + 1 +inline int foo(int x) { + if (x == FORTYTWO) + return FOO_BRANCH(x); + return FORTYTWO; +} +#else +#define BAR_BRANCH(X) (X) + 2 +inline int bar(int x) { + if (x == FORTYTWO) + return BAR_BRANCH(x); + return FORTYTWO; +} +#endif +// CHECK-IMP: remark: importing module './hu-01.h' from 'hu-01.pcm'