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 += ""; @@ -154,7 +156,7 @@ } bool MoveToLine(unsigned LineNo); - bool AvoidConcat(const Token &PrevPrevTok, const Token &PrevTok, + bool AvoidConcat(const Token &PrevPrevTok, const Token &PrevTok, const Token &Tok) { return ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok); } @@ -238,7 +240,7 @@ ++CurLine; return true; } - + return false; } @@ -252,11 +254,11 @@ // Unless we are exiting a #include, make sure to skip ahead to the line the // #include directive was at. SourceManager &SourceMgr = SM; - + PresumedLoc UserLoc = SourceMgr.getPresumedLoc(Loc); if (UserLoc.isInvalid()) return; - + unsigned NewLine = UserLoc.getLine(); if (Reason == PPCallbacks::EnterFile) { @@ -271,7 +273,7 @@ // off by one. We can do better by simply incrementing NewLine here. NewLine += 1; } - + CurLine = NewLine; CurFilename.clear(); @@ -282,7 +284,7 @@ startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); return; } - + if (!Initialized) { WriteLineInfo(CurLine); Initialized = true; @@ -311,6 +313,54 @@ } } +namespace { +/** + * Escape chars which are bad to print inside preprocessed output; e.g. we cannot break lines + * if we're emitting a '#'-style directive (escape both CR and LF). Paths are quoted in some + * instances, so escape quotes. Escaping is with a backslash (and backslashes themselves have + * to be escaped); for CR or LF, we use "\r" or "\n". + */ +std::string sanitizePath(const StringRef& path) { + std::string str(path.size() * 2, '\0'); + size_t len = 0; + for (size_t i = 0; i < path.size(); i++) { + const char c = path[i]; + switch (c) { + case '\\': + str[len++] = '\\'; + str[len++] = '\\'; + break; + case '\r': + str[len++] = '\\'; + str[len++] = 'r'; + break; + case '\n': + str[len++] = '\\'; + str[len++] = 'n'; + break; + case '"': + str[len++] = '\\'; + str[len++] = '"'; + break; + default: + str[len++] = c; + } + } + return str.substr(0, len); +} + +bool tryGetTokenText(std::string* InclusionKeyword, const Token &tok) { + if (tok.getKind() == clang::tok::identifier) { + const auto* idInfo = tok.getIdentifierInfo(); + if (idInfo && idInfo->getNameStart() != nullptr) { + *InclusionKeyword = std::string(idInfo->getNameStart(), idInfo->getLength()); + return true; + } + } + return false; +} +} + void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, @@ -320,10 +370,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 +394,31 @@ // 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. + std::string InclusionKeyword("include"); + tryGetTokenText(&InclusionKeyword, IncludeTok); + OS << "#" << InclusionKeyword << " " + << (IsAngled ? '<' : '"') << FileName << (IsAngled ? '>' : '"'); + + OS << " /* clang -E -dI:"; + + auto SanitizedSearchPath = sanitizePath(SearchPath); + auto SanitizedRelativePath = sanitizePath(RelativePath); + if (SanitizedSearchPath.size() == 0 && SanitizedRelativePath.size() > 0) { + OS << " absolute=\"" << SanitizedRelativePath << "\""; + } else if (SanitizedSearchPath.size() > 0 && SanitizedRelativePath.size() > 0) { + OS << " path=\"" << SanitizedSearchPath << "\""; + } + + OS << " */"; + + setEmittedDirectiveOnThisLine(); + startNewLineIfNeeded(); + } } } @@ -751,7 +826,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"