Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -429,6 +429,8 @@ def dA : Flag<["-"], "dA">, Group; def dD : Flag<["-"], "dD">, Group, Flags<[CC1Option]>, HelpText<"Print macro definitions in -E mode in addition to normal output">; +def dI : Flag<["-"], "dI">, Group, Flags<[CC1Option]>, + HelpText<"Print include directives in -E mode in addition to normal output">; def dM : Flag<["-"], "dM">, Group, Flags<[CC1Option]>, HelpText<"Print macro definitions in -E mode instead of normal output">; def dead__strip : Flag<["-"], "dead_strip">; Index: include/clang/Frontend/PreprocessorOutputOptions.h =================================================================== --- include/clang/Frontend/PreprocessorOutputOptions.h +++ include/clang/Frontend/PreprocessorOutputOptions.h @@ -22,6 +22,7 @@ unsigned UseLineDirectives : 1; ///< Use \#line instead of GCC-style \# N. unsigned ShowMacroComments : 1; ///< Show comments, even in macros. unsigned ShowMacros : 1; ///< Print macro definitions. + unsigned ShowIncludeDirectives : 1; ///< Print includes, imports etc. within preprocessed output. unsigned RewriteIncludes : 1; ///< Preprocess include directives only. public: @@ -32,6 +33,7 @@ UseLineDirectives = 0; ShowMacroComments = 0; ShowMacros = 0; + ShowIncludeDirectives = 0; RewriteIncludes = 0; } }; Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2332,6 +2332,7 @@ Opts.ShowLineMarkers = !Args.hasArg(OPT_P); Opts.ShowMacroComments = Args.hasArg(OPT_CC); Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD); + Opts.ShowIncludeDirectives = Args.hasArg(OPT_dI); Opts.RewriteIncludes = Args.hasArg(OPT_frewrite_includes); Opts.UseLineDirectives = Args.hasArg(OPT_fuse_line_directives); } Index: lib/Frontend/PrintPreprocessedOutput.cpp =================================================================== --- lib/Frontend/PrintPreprocessedOutput.cpp +++ lib/Frontend/PrintPreprocessedOutput.cpp @@ -93,13 +93,15 @@ bool Initialized; bool DisableLineMarkers; bool DumpDefines; + bool DumpIncludeDirectives; bool UseLineDirectives; bool IsFirstFileEntered; public: PrintPPOutputPPCallbacks(Preprocessor &pp, raw_ostream &os, bool lineMarkers, - bool defines, bool UseLineDirectives) + bool defines, bool dumpIncludeDirectives, bool UseLineDirectives) : PP(pp), SM(PP.getSourceManager()), ConcatInfo(PP), OS(os), DisableLineMarkers(lineMarkers), DumpDefines(defines), + DumpIncludeDirectives(dumpIncludeDirectives), UseLineDirectives(UseLineDirectives) { CurLine = 0; CurFilename += ""; @@ -311,6 +313,46 @@ } } +namespace { +/** + * Escape a pathname appropriate for use in preprocessed output, e.g. if using + * '-dI' to print info about inclusions. + */ +std::string sanitizePath(StringRef Path) { + std::string str(Path.size() * 2, '\0'); + size_t len = 0; + for (size_t i = 0; i < Path.size(); i++) { + char Ch = Path[i]; + switch (Ch) { + case '"': // paths are enclosed in quotes; escape them + case '*': // don't allow a "*/" sequence to accidentally open the comment + case '\\': // escape the escape character itself. + str[len++] = '\\'; + // fall through + default: + str[len++] = Ch; + } + } + return str.substr(0, len); +} + +/** + * Try to obtain the text of the token (identifier or other kind). Return + * true if successful, false otherwise. If successful, the "text" parameter + * receives a copy of the token text. + */ +bool tryGetTokenText(StringRef *text, const Token &tok) { + if (tok.getKind() == clang::tok::identifier) { + const auto *idInfo = tok.getIdentifierInfo(); + if (idInfo && idInfo->getName().data() != nullptr) { + *text = idInfo->getName(); + return true; + } + } + return false; +} +} + void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, @@ -320,10 +362,10 @@ StringRef SearchPath, StringRef RelativePath, const Module *Imported) { - // When preprocessing, turn implicit imports into @imports. - // FIXME: This is a stop-gap until a more comprehensive "preprocessing with - // modules" solution is introduced. if (Imported) { + // When preprocessing, turn implicit imports into @imports. + // FIXME: This is a stop-gap until a more comprehensive "preprocessing with + // modules" solution is introduced. startNewLineIfNeeded(); MoveToLine(HashLoc); if (PP.getLangOpts().ObjC2) { @@ -344,6 +386,30 @@ // line immediately. EmittedTokensOnThisLine = true; startNewLineIfNeeded(); + } else { + // Not a module import; it's a more vanilla inclusion of some file using one + // of: #include, #import, #include_next, #include_macros. + if (DumpIncludeDirectives) { + // If we couldn't determine the actual token used in the source, just say + // "#include" in output. + StringRef InclusionKeyword("include"); + tryGetTokenText(&InclusionKeyword, IncludeTok); + + OS << "#" << InclusionKeyword << " " + << (IsAngled ? '<' : '"') + << FileName + << (IsAngled ? '>' : '"') + << " /* clang -E -dI:"; + if (SearchPath.size() == 0 && RelativePath.size() > 0) { + OS << " absolute"; + } else if (SearchPath.size() > 0 && RelativePath.size() > 0) { + OS << " path=\"" << sanitizePath(SearchPath) << "\""; + } + OS << " */"; + + setEmittedDirectiveOnThisLine(); + startNewLineIfNeeded(); + } } } @@ -751,7 +817,8 @@ PP.SetCommentRetentionState(Opts.ShowComments, Opts.ShowMacroComments); PrintPPOutputPPCallbacks *Callbacks = new PrintPPOutputPPCallbacks( - PP, *OS, !Opts.ShowLineMarkers, Opts.ShowMacros, Opts.UseLineDirectives); + PP, *OS, !Opts.ShowLineMarkers, Opts.ShowMacros, Opts.ShowIncludeDirectives, + Opts.UseLineDirectives); // Expand macros in pragmas with -fms-extensions. The assumption is that // the majority of pragmas in such a file will be Microsoft pragmas. Index: test/Preprocessor/dump_import.h =================================================================== --- /dev/null +++ test/Preprocessor/dump_import.h @@ -0,0 +1 @@ +#define DUMP_IMPORT_TESTVAL 1 Index: test/Preprocessor/dump_import.m =================================================================== --- /dev/null +++ test/Preprocessor/dump_import.m @@ -0,0 +1,2 @@ +// RUN: %clang_cc1 -E -dI %s -o - | grep '^#import' +#import "dump_import.h" Index: test/Preprocessor/dump_include.h =================================================================== --- /dev/null +++ test/Preprocessor/dump_include.h @@ -0,0 +1,2 @@ +#pragma once +#define DUMP_INCLUDE_TESTVAL 1 Index: test/Preprocessor/dump_include.c =================================================================== --- /dev/null +++ test/Preprocessor/dump_include.c @@ -0,0 +1,2 @@ +// RUN: %clang_cc1 -E -dI %s -o - | grep '^#include' +#include "dump_include.h"