7c611aed7c
This adds few fixes, but mostly it updates the rewrite-includes patch. Sadly upstream has decided to rename the option name for whatever reason, so if unchanged 12.2 would be the only clang with the option named this way, causing further problems e.g. with icecream. So it would be very nice to get this into 12.2. As it is an isolated feature, it should be low-risk. OBS-URL: https://build.opensuse.org/request/show/127882 OBS-URL: https://build.opensuse.org/package/show/devel:tools:compiler/llvm?expand=0&rev=155
822 lines
33 KiB
Diff
822 lines
33 KiB
Diff
diff --git tools/clang/include/clang/Driver/CC1Options.td tools/clang/include/clang/Driver/CC1Options.td
|
|
index 83c988a..b7deed0 100644
|
|
--- tools/clang/include/clang/Driver/CC1Options.td
|
|
+++ tools/clang/include/clang/Driver/CC1Options.td
|
|
@@ -814,6 +814,8 @@ def dM : Flag<"-dM">,
|
|
HelpText<"Print macro definitions in -E mode instead of normal output">;
|
|
def dD : Flag<"-dD">,
|
|
HelpText<"Print macro definitions in -E mode in addition to normal output">;
|
|
+def frewrite_includes : Flag<"-frewrite-includes">,
|
|
+ HelpText<"Expand includes without full preprocessing">;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// OpenCL Options
|
|
diff --git tools/clang/include/clang/Driver/Options.td tools/clang/include/clang/Driver/Options.td
|
|
index b796771..f58d824 100644
|
|
--- tools/clang/include/clang/Driver/Options.td
|
|
+++ tools/clang/include/clang/Driver/Options.td
|
|
@@ -362,6 +362,9 @@ def fno_trapping_math : Flag<"-fno-trapping-math">, Group<f_Group>;
|
|
def ffor_scope : Flag<"-ffor-scope">, Group<f_Group>;
|
|
def fno_for_scope : Flag<"-fno-for-scope">, Group<f_Group>;
|
|
|
|
+def frewrite_includes : Flag<"-frewrite-includes">, Group<f_Group>;
|
|
+def fno_rewrite_includes : Flag<"-fno-rewrite-includes">, Group<f_Group>;
|
|
+
|
|
def ffreestanding : Flag<"-ffreestanding">, Group<f_Group>;
|
|
def fgnu_keywords : Flag<"-fgnu-keywords">, Group<f_Group>;
|
|
def fgnu89_inline : Flag<"-fgnu89-inline">, Group<f_Group>;
|
|
diff --git tools/clang/include/clang/Frontend/FrontendOptions.h tools/clang/include/clang/Frontend/FrontendOptions.h
|
|
index 888388c..48cd317 100644
|
|
--- tools/clang/include/clang/Frontend/FrontendOptions.h
|
|
+++ tools/clang/include/clang/Frontend/FrontendOptions.h
|
|
@@ -42,7 +42,7 @@ namespace frontend {
|
|
PrintDeclContext, ///< Print DeclContext and their Decls.
|
|
PrintPreamble, ///< Print the "preamble" of the input file
|
|
PrintPreprocessedInput, ///< -E mode.
|
|
- RewriteMacros, ///< Expand macros but not #includes.
|
|
+ RewriteMacros, ///< Expand macros but not \#includes.
|
|
RewriteObjC, ///< ObjC->C Rewriter.
|
|
RewriteTest, ///< Rewriter playground
|
|
RunAnalysis, ///< Run one or more source code analyses.
|
|
diff --git tools/clang/include/clang/Frontend/PreprocessorOutputOptions.h tools/clang/include/clang/Frontend/PreprocessorOutputOptions.h
|
|
index 1eda0d4..31724cd 100644
|
|
--- tools/clang/include/clang/Frontend/PreprocessorOutputOptions.h
|
|
+++ tools/clang/include/clang/Frontend/PreprocessorOutputOptions.h
|
|
@@ -21,6 +21,7 @@ public:
|
|
unsigned ShowLineMarkers : 1; ///< Show #line markers.
|
|
unsigned ShowMacroComments : 1; ///< Show comments, even in macros.
|
|
unsigned ShowMacros : 1; ///< Print macro definitions.
|
|
+ unsigned RewriteIncludes : 1; ///< Preprocess include directives only.
|
|
|
|
public:
|
|
PreprocessorOutputOptions() {
|
|
@@ -29,6 +30,7 @@ public:
|
|
ShowLineMarkers = 1;
|
|
ShowMacroComments = 0;
|
|
ShowMacros = 0;
|
|
+ RewriteIncludes = 0;
|
|
}
|
|
};
|
|
|
|
diff --git tools/clang/include/clang/Lex/Preprocessor.h tools/clang/include/clang/Lex/Preprocessor.h
|
|
index 055008f..638b4aa 100644
|
|
--- tools/clang/include/clang/Lex/Preprocessor.h
|
|
+++ tools/clang/include/clang/Lex/Preprocessor.h
|
|
@@ -121,6 +121,13 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
|
|
/// DisableMacroExpansion - True if macro expansion is disabled.
|
|
bool DisableMacroExpansion : 1;
|
|
|
|
+ /// MacroExpansionInDirectivesOverride - Temporarily disables
|
|
+ /// DisableMacroExpansion (i.e. enables expansion) when parsing preprocessor
|
|
+ /// directives.
|
|
+ bool MacroExpansionInDirectivesOverride : 1;
|
|
+
|
|
+ class ResetMacroExpansionHelper;
|
|
+
|
|
/// \brief Whether we have already loaded macros from the external source.
|
|
mutable bool ReadMacrosFromExternalSource : 1;
|
|
|
|
@@ -634,6 +641,12 @@ public:
|
|
while (Result.getKind() == tok::comment);
|
|
}
|
|
|
|
+ /// Disables macro expansion everywhere except for preprocessor directives.
|
|
+ void SetMacroExpansionOnlyInDirectives() {
|
|
+ DisableMacroExpansion = true;
|
|
+ MacroExpansionInDirectivesOverride = true;
|
|
+ }
|
|
+
|
|
/// LookAhead - This peeks ahead N tokens and returns that token without
|
|
/// consuming any tokens. LookAhead(0) returns the next token that would be
|
|
/// returned by Lex(), LookAhead(1) returns the token after it, etc. This
|
|
diff --git tools/clang/include/clang/Rewrite/FrontendActions.h tools/clang/include/clang/Rewrite/FrontendActions.h
|
|
index 6e9ecac..ea876d9 100644
|
|
--- tools/clang/include/clang/Rewrite/FrontendActions.h
|
|
+++ tools/clang/include/clang/Rewrite/FrontendActions.h
|
|
@@ -73,6 +73,11 @@ protected:
|
|
void ExecuteAction();
|
|
};
|
|
|
|
+class RewriteIncludesAction : public PreprocessorFrontendAction {
|
|
+protected:
|
|
+ void ExecuteAction();
|
|
+};
|
|
+
|
|
} // end namespace clang
|
|
|
|
#endif
|
|
diff --git tools/clang/include/clang/Rewrite/Rewriters.h tools/clang/include/clang/Rewrite/Rewriters.h
|
|
index 203b9bc..f5ade5a 100644
|
|
--- tools/clang/include/clang/Rewrite/Rewriters.h
|
|
+++ tools/clang/include/clang/Rewrite/Rewriters.h
|
|
@@ -18,6 +18,7 @@
|
|
|
|
namespace clang {
|
|
class Preprocessor;
|
|
+class PreprocessorOutputOptions;
|
|
|
|
/// RewriteMacrosInInput - Implement -rewrite-macros mode.
|
|
void RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS);
|
|
@@ -25,6 +26,10 @@ void RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS);
|
|
/// DoRewriteTest - A simple test for the TokenRewriter class.
|
|
void DoRewriteTest(Preprocessor &PP, raw_ostream *OS);
|
|
|
|
+/// RewriteIncludesInInput - Implement -frewrite-includes mode.
|
|
+void RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
|
|
+ const PreprocessorOutputOptions &Opts);
|
|
+
|
|
} // end namespace clang
|
|
|
|
#endif
|
|
diff --git tools/clang/lib/Driver/Tools.cpp tools/clang/lib/Driver/Tools.cpp
|
|
index 47b5294..c0504e7 100644
|
|
--- tools/clang/lib/Driver/Tools.cpp
|
|
+++ tools/clang/lib/Driver/Tools.cpp
|
|
@@ -2106,6 +2106,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
|
if (Args.getLastArg(options::OPT_fapple_kext))
|
|
CmdArgs.push_back("-fapple-kext");
|
|
|
|
+ if (Args.hasFlag(options::OPT_frewrite_includes,
|
|
+ options::OPT_fno_rewrite_includes, false))
|
|
+ CmdArgs.push_back("-frewrite-includes");
|
|
+
|
|
Args.AddLastArg(CmdArgs, options::OPT_fobjc_sender_dependent_dispatch);
|
|
Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_print_source_range_info);
|
|
Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_parseable_fixits);
|
|
diff --git tools/clang/lib/Frontend/CompilerInvocation.cpp tools/clang/lib/Frontend/CompilerInvocation.cpp
|
|
index 4c5b063..28935ce 100644
|
|
--- tools/clang/lib/Frontend/CompilerInvocation.cpp
|
|
+++ tools/clang/lib/Frontend/CompilerInvocation.cpp
|
|
@@ -2058,6 +2058,7 @@ static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts,
|
|
Opts.ShowLineMarkers = !Args.hasArg(OPT_P);
|
|
Opts.ShowMacroComments = Args.hasArg(OPT_CC);
|
|
Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD);
|
|
+ Opts.RewriteIncludes = Args.hasArg(OPT_frewrite_includes);
|
|
}
|
|
|
|
static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args) {
|
|
diff --git tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
|
|
index 07d2b8d..0824978 100644
|
|
--- tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
|
|
+++ tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
|
|
@@ -71,7 +71,12 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) {
|
|
|
|
case PrintDeclContext: return new DeclContextPrintAction();
|
|
case PrintPreamble: return new PrintPreambleAction();
|
|
- case PrintPreprocessedInput: return new PrintPreprocessedAction();
|
|
+ case PrintPreprocessedInput: {
|
|
+ if (CI.getPreprocessorOutputOpts().RewriteIncludes)
|
|
+ return new RewriteIncludesAction();
|
|
+ return new PrintPreprocessedAction();
|
|
+ }
|
|
+
|
|
case RewriteMacros: return new RewriteMacrosAction();
|
|
case RewriteObjC: return new RewriteObjCAction();
|
|
case RewriteTest: return new RewriteTestAction();
|
|
diff --git tools/clang/lib/Lex/Lexer.cpp tools/clang/lib/Lex/Lexer.cpp
|
|
index 535a852..aa424b4 100644
|
|
--- tools/clang/lib/Lex/Lexer.cpp
|
|
+++ tools/clang/lib/Lex/Lexer.cpp
|
|
@@ -2022,7 +2022,7 @@ bool Lexer::SaveBCPLComment(Token &Result, const char *CurPtr) {
|
|
// directly.
|
|
FormTokenWithChars(Result, CurPtr, tok::comment);
|
|
|
|
- if (!ParsingPreprocessorDirective)
|
|
+ if (!ParsingPreprocessorDirective || LexingRawMode)
|
|
return true;
|
|
|
|
// If this BCPL-style comment is in a macro definition, transmogrify it into
|
|
@@ -2625,7 +2625,8 @@ LexNextToken:
|
|
ParsingPreprocessorDirective = false;
|
|
|
|
// Restore comment saving mode, in case it was disabled for directive.
|
|
- SetCommentRetentionState(PP->getCommentRetentionState());
|
|
+ if (!LexingRawMode)
|
|
+ SetCommentRetentionState(PP->getCommentRetentionState());
|
|
|
|
// Since we consumed a newline, we are back at the start of a line.
|
|
IsAtStartOfLine = true;
|
|
diff --git tools/clang/lib/Lex/PPDirectives.cpp tools/clang/lib/Lex/PPDirectives.cpp
|
|
index 625a204..29a8614 100644
|
|
--- tools/clang/lib/Lex/PPDirectives.cpp
|
|
+++ tools/clang/lib/Lex/PPDirectives.cpp
|
|
@@ -553,6 +553,22 @@ const FileEntry *Preprocessor::LookupFile(
|
|
// Preprocessor Directive Handling.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
+class Preprocessor::ResetMacroExpansionHelper
|
|
+{
|
|
+public:
|
|
+ ResetMacroExpansionHelper(Preprocessor*pp)
|
|
+ : PP(pp), save(pp->DisableMacroExpansion) {
|
|
+ if (pp->MacroExpansionInDirectivesOverride)
|
|
+ pp->DisableMacroExpansion = false;
|
|
+ }
|
|
+ ~ResetMacroExpansionHelper() {
|
|
+ PP->DisableMacroExpansion = save;
|
|
+ }
|
|
+private:
|
|
+ Preprocessor* PP;
|
|
+ bool save;
|
|
+};
|
|
+
|
|
/// HandleDirective - This callback is invoked when the lexer sees a # token
|
|
/// at the start of a line. This consumes the directive, modifies the
|
|
/// lexer/preprocessor state, and advances the lexer(s) so that the next token
|
|
@@ -604,6 +620,10 @@ void Preprocessor::HandleDirective(Token &Result) {
|
|
Diag(Result, diag::ext_embedded_directive);
|
|
}
|
|
|
|
+ // temporarily enable macro expansion if set so
|
|
+ // and reset to previous state when returning from this function
|
|
+ ResetMacroExpansionHelper helper(this);
|
|
+
|
|
TryAgain:
|
|
switch (Result.getKind()) {
|
|
case tok::eod:
|
|
diff --git tools/clang/lib/Lex/Preprocessor.cpp tools/clang/lib/Lex/Preprocessor.cpp
|
|
index 06e5685..f5d7a51 100644
|
|
--- tools/clang/lib/Lex/Preprocessor.cpp
|
|
+++ tools/clang/lib/Lex/Preprocessor.cpp
|
|
@@ -134,6 +134,7 @@ void Preprocessor::Initialize(const TargetInfo &Target) {
|
|
|
|
// Macro expansion is enabled.
|
|
DisableMacroExpansion = false;
|
|
+ MacroExpansionInDirectivesOverride = false;
|
|
InMacroArgs = false;
|
|
InMacroArgPreExpansion = false;
|
|
NumCachedTokenLexers = 0;
|
|
diff --git tools/clang/lib/Rewrite/CMakeLists.txt tools/clang/lib/Rewrite/CMakeLists.txt
|
|
index 2a05040..8070ba2 100644
|
|
--- tools/clang/lib/Rewrite/CMakeLists.txt
|
|
+++ tools/clang/lib/Rewrite/CMakeLists.txt
|
|
@@ -6,6 +6,7 @@ add_clang_library(clangRewrite
|
|
FrontendActions.cpp
|
|
HTMLPrint.cpp
|
|
HTMLRewrite.cpp
|
|
+ RewriteIncludes.cpp
|
|
RewriteMacros.cpp
|
|
RewriteModernObjC.cpp
|
|
RewriteObjC.cpp
|
|
diff --git tools/clang/lib/Rewrite/FrontendActions.cpp tools/clang/lib/Rewrite/FrontendActions.cpp
|
|
index 1753325..e462671 100644
|
|
--- tools/clang/lib/Rewrite/FrontendActions.cpp
|
|
+++ tools/clang/lib/Rewrite/FrontendActions.cpp
|
|
@@ -181,3 +181,11 @@ void RewriteTestAction::ExecuteAction() {
|
|
|
|
DoRewriteTest(CI.getPreprocessor(), OS);
|
|
}
|
|
+
|
|
+void RewriteIncludesAction::ExecuteAction() {
|
|
+ CompilerInstance &CI = getCompilerInstance();
|
|
+ raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile());
|
|
+ if (!OS) return;
|
|
+
|
|
+ RewriteIncludesInInput(CI.getPreprocessor(), OS, CI.getPreprocessorOutputOpts());
|
|
+}
|
|
diff --git tools/clang/lib/Rewrite/RewriteIncludes.cpp tools/clang/lib/Rewrite/RewriteIncludes.cpp
|
|
new file mode 100644
|
|
index 0000000..b7de20c
|
|
--- /dev/null
|
|
+++ tools/clang/lib/Rewrite/RewriteIncludes.cpp
|
|
@@ -0,0 +1,362 @@
|
|
+//===--- RewriteIncludes.cpp - Rewrite includes into their expansions -----===//
|
|
+//
|
|
+// The LLVM Compiler Infrastructure
|
|
+//
|
|
+// This file is distributed under the University of Illinois Open Source
|
|
+// License. See LICENSE.TXT for details.
|
|
+//
|
|
+//===----------------------------------------------------------------------===//
|
|
+//
|
|
+// This code rewrites include invocations into their expansions. This gives you
|
|
+// a file with all included files merged into it.
|
|
+//
|
|
+//===----------------------------------------------------------------------===//
|
|
+
|
|
+#include "clang/Rewrite/Rewriters.h"
|
|
+#include "clang/Lex/Preprocessor.h"
|
|
+#include "clang/Basic/SourceManager.h"
|
|
+#include "clang/Frontend/PreprocessorOutputOptions.h"
|
|
+#include "llvm/Support/raw_ostream.h"
|
|
+
|
|
+using namespace clang;
|
|
+using namespace llvm;
|
|
+
|
|
+namespace {
|
|
+
|
|
+/// Class representing the whole rewriting process and global data for it.
|
|
+class IncludeRewriter : public PPCallbacks {
|
|
+ /// Information about which #includes were actually performed,
|
|
+ /// created by preprocessor callbacks.
|
|
+ struct FileChange {
|
|
+ SourceLocation From;
|
|
+ FileID Id;
|
|
+ SrcMgr::CharacteristicKind Type;
|
|
+ };
|
|
+ Preprocessor &PP;
|
|
+ SourceManager &SM;
|
|
+ raw_ostream &OS;
|
|
+ bool DisableLineMarkers;
|
|
+ bool UseLineDirective;
|
|
+ typedef std::map<unsigned, FileChange> FileChangeMap;
|
|
+ FileChangeMap FileChanges;
|
|
+ unsigned LastInsertedFileChange;
|
|
+ class File;
|
|
+public:
|
|
+ IncludeRewriter(Preprocessor &PP, raw_ostream &OS,
|
|
+ const PreprocessorOutputOptions &Opts);
|
|
+ void Process();
|
|
+ virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
|
|
+ SrcMgr::CharacteristicKind FileType,
|
|
+ FileID PrevFID);
|
|
+ virtual void InclusionDirective(SourceLocation HashLoc,
|
|
+ const Token &IncludeTok,
|
|
+ StringRef FileName,
|
|
+ bool IsAngled,
|
|
+ const FileEntry *File,
|
|
+ SourceLocation EndLoc,
|
|
+ StringRef SearchPath,
|
|
+ StringRef RelativePath);
|
|
+private:
|
|
+ const FileChange* FindFileChangeLocation(SourceLocation Loc) const;
|
|
+};
|
|
+
|
|
+/// Class representing the process of rewriting one source file.
|
|
+class IncludeRewriter::File {
|
|
+ IncludeRewriter* Rewriter;
|
|
+ Preprocessor &PP;
|
|
+ SourceManager &SM;
|
|
+ raw_ostream &OS;
|
|
+ FileID FileId;
|
|
+ SrcMgr::CharacteristicKind Type;
|
|
+ const MemoryBuffer *FromFile;
|
|
+ const char* FileName;
|
|
+ SourceLocation StartLocation;
|
|
+ bool Invalid;
|
|
+ const char* EOL;
|
|
+ OwningPtr<Lexer> RawLex;
|
|
+ // file position from which the content has not yet been copied to the output
|
|
+ unsigned NextToWrite;
|
|
+ int Line; // current input file line number
|
|
+public:
|
|
+ File(IncludeRewriter* Rewriter, FileID FileId,
|
|
+ SrcMgr::CharacteristicKind Type);
|
|
+ bool Process();
|
|
+private:
|
|
+ void WriteLineInfo(StringRef Extra = StringRef());
|
|
+ void OutputContentUpTo(unsigned WriteEnd, bool EnsureNewline = false);
|
|
+ void CommentOutDirective(const Token& StartToken);
|
|
+ StringRef NextIdentifierName(Token& RawToken);
|
|
+ void DetectEOL();
|
|
+ unsigned CountNewLines(const char* Pos, int Len);
|
|
+};
|
|
+
|
|
+} // end anonymous namespace
|
|
+
|
|
+IncludeRewriter::IncludeRewriter(Preprocessor &pp, raw_ostream &os,
|
|
+ const PreprocessorOutputOptions &Opts)
|
|
+ : PP(pp), SM(PP.getSourceManager()), OS(os),
|
|
+ DisableLineMarkers(!Opts.ShowLineMarkers), LastInsertedFileChange(0) {
|
|
+ // If we're in microsoft mode, use normal #line instead of line markers.
|
|
+ UseLineDirective = PP.getLangOpts().MicrosoftExt;
|
|
+}
|
|
+
|
|
+/// FileChanged - Whenever the preprocessor enters or exits a #include file
|
|
+/// it invokes this handler.
|
|
+void IncludeRewriter::FileChanged(SourceLocation Loc,
|
|
+ FileChangeReason Reason,
|
|
+ SrcMgr::CharacteristicKind NewFileType,
|
|
+ FileID) {
|
|
+ if (Reason == EnterFile) {
|
|
+ // InclusionDirective() has already been called, add more info
|
|
+ FileID Id = FullSourceLoc(Loc,SM).getFileID();
|
|
+ if (LastInsertedFileChange != 0) { // there may be e.g. "<built-in>" first
|
|
+ FileChange& Ref = FileChanges[LastInsertedFileChange];
|
|
+ Ref.Id = Id;
|
|
+ Ref.Type = NewFileType;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/// This should be called whenever the preprocessor encounters include
|
|
+/// directives. It does not say whether the file has been included, but it
|
|
+/// provides more information about the directive (hash location istead
|
|
+/// of location inside the included file. It is assumed that the matching
|
|
+/// FileChanged() is called after this (if at all).
|
|
+void IncludeRewriter::InclusionDirective(SourceLocation HashLoc,
|
|
+ const Token &/*IncludeTok*/,
|
|
+ StringRef /*FileName*/,
|
|
+ bool /*IsAngled*/,
|
|
+ const FileEntry* /*File*/,
|
|
+ SourceLocation /*EndLoc*/,
|
|
+ StringRef /*SearchPath*/,
|
|
+ StringRef /*RelativePath*/) {
|
|
+ FileChange Change;
|
|
+ Change.From = HashLoc;
|
|
+ LastInsertedFileChange = HashLoc.getRawEncoding();
|
|
+ FileChanges[LastInsertedFileChange] = Change;
|
|
+}
|
|
+
|
|
+const IncludeRewriter::FileChange*
|
|
+IncludeRewriter::FindFileChangeLocation(SourceLocation Loc) const {
|
|
+ FileChangeMap ::const_iterator Find = FileChanges.find(Loc.getRawEncoding());
|
|
+ if(Find != FileChanges.end())
|
|
+ return &Find->second;
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+void IncludeRewriter::Process() {
|
|
+ File MainFile(this, SM.getMainFileID(), SrcMgr::C_User);
|
|
+ MainFile.Process();
|
|
+}
|
|
+
|
|
+
|
|
+IncludeRewriter::File::File(IncludeRewriter* Rewriter,
|
|
+ FileID FileId,
|
|
+ SrcMgr::CharacteristicKind Type)
|
|
+ : Rewriter(Rewriter)
|
|
+ , PP(Rewriter->PP)
|
|
+ , SM(Rewriter->SM)
|
|
+ , OS(Rewriter->OS)
|
|
+ , FileId(FileId)
|
|
+ , Type(Type)
|
|
+ , Invalid(true)
|
|
+ , NextToWrite(0)
|
|
+ , Line(1) {
|
|
+
|
|
+ StartLocation = SM.getLocForStartOfFile(FileId);
|
|
+ FileName = SM.getBufferName(StartLocation, &Invalid);
|
|
+ if (Invalid)
|
|
+ return;
|
|
+
|
|
+ // Use a raw lexer to analyze the input file, incrementally copying parts of
|
|
+ // it and including content of included files recursively.
|
|
+ FromFile = SM.getBuffer(FileId);
|
|
+ RawLex.reset( new Lexer(FileId, FromFile, PP.getSourceManager(), PP.getLangOpts()));
|
|
+ RawLex->SetCommentRetentionState(false);
|
|
+ DetectEOL();
|
|
+
|
|
+ WriteLineInfo(" 1");
|
|
+}
|
|
+
|
|
+bool IncludeRewriter::File::Process()
|
|
+{
|
|
+ if (Invalid)
|
|
+ return false;
|
|
+ if (SM.getFileIDSize(FileId) == 0)
|
|
+ return true;
|
|
+
|
|
+ Token RawToken;
|
|
+ RawLex->LexFromRawLexer(RawToken);
|
|
+
|
|
+ // TODO: It might be beneficial to have a switch that removes content of lines
|
|
+ // that are irrevelant for compile, i.e. comments. Comments are usually
|
|
+ // a significant part of the resulting file and cleaning up such lines would
|
|
+ // significantly reduce the size of the resulting file without having any
|
|
+ // effect on any following usage (with the exception of human inspection).
|
|
+ while (RawToken.isNot(tok::eof)) {
|
|
+ if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) {
|
|
+ RawLex->setParsingPreprocessorDirective(true);
|
|
+ Token HashToken = RawToken;
|
|
+ RawLex->LexFromRawLexer(RawToken);
|
|
+ if (RawToken.is(tok::raw_identifier))
|
|
+ PP.LookUpIdentifierInfo(RawToken);
|
|
+ if (RawToken.is(tok::identifier)) {
|
|
+ switch (RawToken.getIdentifierInfo()->getPPKeywordID()) {
|
|
+ case tok::pp_include:
|
|
+ case tok::pp_include_next:
|
|
+ case tok::pp_import: {
|
|
+ // keep the directive in, commented out
|
|
+ // (clang sometimes optimizes and does not repeatedly include some
|
|
+ // files even though it should, so all includes need to be
|
|
+ // commented, otherwise valid directives would be left in)
|
|
+ CommentOutDirective(HashToken);
|
|
+ // fix up lineinfo, commenting out has added lines
|
|
+ bool NeedFixup = true;
|
|
+ if (const FileChange* Change = Rewriter->FindFileChangeLocation(
|
|
+ HashToken.getLocation())) {
|
|
+ // now include and recursively process the file
|
|
+ File IncludedFile(Rewriter, Change->Id, Change->Type);
|
|
+ if (IncludedFile.Process()) {
|
|
+ // and set lineinfo back to this file, if the nested one was
|
|
+ // actually included
|
|
+ WriteLineInfo(" 2");
|
|
+ NeedFixup = false;
|
|
+ }
|
|
+ }
|
|
+ if(NeedFixup)
|
|
+ WriteLineInfo();
|
|
+ break;
|
|
+ }
|
|
+ case tok::pp_pragma: {
|
|
+ StringRef Identifier = NextIdentifierName(RawToken);
|
|
+ if (Identifier == "clang" || Identifier == "GCC") {
|
|
+ if (NextIdentifierName(RawToken) == "system_header") {
|
|
+ // keep the directive in, commented out
|
|
+ CommentOutDirective(HashToken);
|
|
+ // update our own type
|
|
+ Type = SM.getFileCharacteristic(RawToken.getLocation());
|
|
+ WriteLineInfo();
|
|
+ }
|
|
+ } else if (Identifier == "once") {
|
|
+ // keep the directive in, commented out
|
|
+ CommentOutDirective(HashToken);
|
|
+ WriteLineInfo();
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ RawLex->setParsingPreprocessorDirective(false);
|
|
+ RawLex->SetCommentRetentionState(false);
|
|
+ }
|
|
+ RawLex->LexFromRawLexer(RawToken);
|
|
+ }
|
|
+ OutputContentUpTo(SM.getFileIDSize(FileId), true);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void IncludeRewriter::File::DetectEOL() {
|
|
+ // detect what line endings the file uses, so that added content does not mix
|
|
+ // the style
|
|
+ const char* Pos = strchr(FromFile->getBufferStart(), '\n');
|
|
+ if (Pos == NULL)
|
|
+ EOL = "\n";
|
|
+ else if (Pos+1 < FromFile->getBufferEnd() && *(Pos+1) == '\r')
|
|
+ EOL = "\n\r";
|
|
+ else if (Pos-1 >= FromFile->getBufferStart() && *(Pos+1) == '\r')
|
|
+ EOL = "\r\n";
|
|
+ else
|
|
+ EOL = "\n";
|
|
+}
|
|
+
|
|
+void IncludeRewriter::File::WriteLineInfo(StringRef Extra) {
|
|
+ if (Rewriter->DisableLineMarkers) {
|
|
+ OS << EOL; // Still need to at least separate lines.
|
|
+ return;
|
|
+ }
|
|
+ // Emit #line directives or GNU line markers depending on what mode we're in.
|
|
+ if (Rewriter->UseLineDirective) {
|
|
+ OS << "#line" << ' ' << Line << ' ' << '"' << FileName << '"';
|
|
+ } else {
|
|
+ OS << '#' << ' ' << Line << ' ' << '"' << FileName << '"';
|
|
+ if (!Extra.empty())
|
|
+ OS << Extra;
|
|
+ if (Type == SrcMgr::C_System)
|
|
+ OS << " 3";
|
|
+ else if (Type == SrcMgr::C_ExternCSystem)
|
|
+ OS << " 3 4";
|
|
+ }
|
|
+ OS << EOL;
|
|
+}
|
|
+
|
|
+inline unsigned IncludeRewriter::File::CountNewLines(const char* Pos, int Len) {
|
|
+ const char* End = Pos + Len;
|
|
+ unsigned Lines = 0;
|
|
+ --Pos;
|
|
+ while ((Pos = static_cast<const char*>(memchr(Pos + 1, '\n', End - Pos - 1))))
|
|
+ ++Lines;
|
|
+ return Lines;
|
|
+}
|
|
+
|
|
+/// Copies next not yet written file content up (and not including) to writeEnd.
|
|
+void IncludeRewriter::File::OutputContentUpTo(unsigned WriteEnd,
|
|
+ bool EnsureNewline) {
|
|
+ if (WriteEnd > NextToWrite) {
|
|
+ OS.write(FromFile->getBufferStart() + NextToWrite, WriteEnd - NextToWrite);
|
|
+ // count lines manually, it's faster than getPresumedLoc()
|
|
+ Line += CountNewLines(FromFile->getBufferStart() + NextToWrite,
|
|
+ WriteEnd - NextToWrite);
|
|
+ if (EnsureNewline) {
|
|
+ char LastChar = FromFile->getBufferStart()[WriteEnd - 1];
|
|
+ if (LastChar != '\n' && LastChar != '\r')
|
|
+ OS << EOL;
|
|
+ }
|
|
+ NextToWrite = WriteEnd;
|
|
+ }
|
|
+}
|
|
+
|
|
+void IncludeRewriter::File::CommentOutDirective(const Token& StartToken) {
|
|
+ OutputContentUpTo(SM.getFileOffset(StartToken.getLocation()));
|
|
+ Token DirectiveToken;
|
|
+ do {
|
|
+ RawLex->LexFromRawLexer(DirectiveToken);
|
|
+ } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof));
|
|
+ OS << "#if 0 /* expanded by -frewrite-includes */" << EOL;
|
|
+ OutputContentUpTo(SM.getFileOffset(DirectiveToken.getLocation())
|
|
+ + DirectiveToken.getLength());
|
|
+ OS << "#endif /* expanded by -frewrite-includes */" << EOL;
|
|
+}
|
|
+
|
|
+StringRef IncludeRewriter::File::NextIdentifierName(Token& RawToken) {
|
|
+ RawLex->LexFromRawLexer(RawToken);
|
|
+ if (RawToken.is(tok::raw_identifier))
|
|
+ PP.LookUpIdentifierInfo(RawToken);
|
|
+ if (RawToken.is(tok::identifier))
|
|
+ return RawToken.getIdentifierInfo()->getName();
|
|
+ return StringRef();
|
|
+}
|
|
+
|
|
+/// RewriteIncludesInInput - Implement -frewrite-includes mode.
|
|
+void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
|
|
+ const PreprocessorOutputOptions &Opts) {
|
|
+ IncludeRewriter* Rewrite = new IncludeRewriter(PP, *OS, Opts);
|
|
+ PP.addPPCallbacks(Rewrite);
|
|
+
|
|
+ // First let the preprocessor process the entire file and call callbacks.
|
|
+ // Callbacks will record which #include's were actually performed.
|
|
+ PP.EnterMainSourceFile();
|
|
+ Token Tok;
|
|
+ // Only preprocessor directives matter here, so disable macro expansion
|
|
+ // everywhere else as an optimization.
|
|
+ // TODO: It would be even faster if the preprocessor could be switched
|
|
+ // to a mode where it would parse only preprocessor directives and comments,
|
|
+ // nothing else matters for parsing or processing.
|
|
+ PP.SetMacroExpansionOnlyInDirectives();
|
|
+ do {
|
|
+ PP.Lex(Tok);
|
|
+ } while (Tok.isNot(tok::eof));
|
|
+ Rewrite->Process();
|
|
+ OS->flush();
|
|
+}
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes1.h tools/clang/test/Frontend/Inputs/rewrite-includes1.h
|
|
new file mode 100644
|
|
index 0000000..1b6c80d
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/Inputs/rewrite-includes1.h
|
|
@@ -0,0 +1,3 @@
|
|
+#pragma clang system_header
|
|
+included_line1
|
|
+#include "rewrite-includes2.h"
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes2.h tools/clang/test/Frontend/Inputs/rewrite-includes2.h
|
|
new file mode 100644
|
|
index 0000000..1114e51
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/Inputs/rewrite-includes2.h
|
|
@@ -0,0 +1 @@
|
|
+included_line2
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes3.h tools/clang/test/Frontend/Inputs/rewrite-includes3.h
|
|
new file mode 100644
|
|
index 0000000..3757bc8
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/Inputs/rewrite-includes3.h
|
|
@@ -0,0 +1 @@
|
|
+included_line3
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes4.h tools/clang/test/Frontend/Inputs/rewrite-includes4.h
|
|
new file mode 100644
|
|
index 0000000..b4e25d2
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/Inputs/rewrite-includes4.h
|
|
@@ -0,0 +1 @@
|
|
+included_line4
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes5.h tools/clang/test/Frontend/Inputs/rewrite-includes5.h
|
|
new file mode 100644
|
|
index 0000000..934bf41
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/Inputs/rewrite-includes5.h
|
|
@@ -0,0 +1 @@
|
|
+included_line5
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes6.h tools/clang/test/Frontend/Inputs/rewrite-includes6.h
|
|
new file mode 100644
|
|
index 0000000..c18e501
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/Inputs/rewrite-includes6.h
|
|
@@ -0,0 +1,2 @@
|
|
+#pragma once
|
|
+included_line6
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes7.h tools/clang/test/Frontend/Inputs/rewrite-includes7.h
|
|
new file mode 100644
|
|
index 0000000..85428fd
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/Inputs/rewrite-includes7.h
|
|
@@ -0,0 +1,4 @@
|
|
+#ifndef REWRITE_INCLUDES7_H
|
|
+#define REWRITE_INCLUDES7_H
|
|
+included_line7
|
|
+#endif
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes8.h tools/clang/test/Frontend/Inputs/rewrite-includes8.h
|
|
new file mode 100644
|
|
index 0000000..7ab5fd9
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/Inputs/rewrite-includes8.h
|
|
@@ -0,0 +1 @@
|
|
+included_line8
|
|
\ No newline at end of file
|
|
diff --git tools/clang/test/Frontend/Inputs/rewrite-includes9.h tools/clang/test/Frontend/Inputs/rewrite-includes9.h
|
|
new file mode 100644
|
|
index 0000000..e69de29
|
|
diff --git tools/clang/test/Frontend/rewrite-includes.c tools/clang/test/Frontend/rewrite-includes.c
|
|
new file mode 100644
|
|
index 0000000..67a8802
|
|
--- /dev/null
|
|
+++ tools/clang/test/Frontend/rewrite-includes.c
|
|
@@ -0,0 +1,105 @@
|
|
+// RUN: %clang_cc1 -verify -E -frewrite-includes -DFIRST -I %S/Inputs %s -o - | FileCheck -strict-whitespace %s
|
|
+
|
|
+// STARTCOMPARE
|
|
+#define A(a,b) a ## b
|
|
+A(1,2)
|
|
+#include "rewrite-includes1.h"
|
|
+#ifdef FIRST
|
|
+#define HEADER "rewrite-includes3.h"
|
|
+#include HEADER
|
|
+#else
|
|
+#include "rewrite-includes4.h"
|
|
+#endif
|
|
+#/**/include /**/ "rewrite-includes5.h" /**/ \
|
|
+
|
|
+#include "rewrite-includes6.h" // comment
|
|
+
|
|
+#include "rewrite-includes6.h" /* comment
|
|
+ continues */
|
|
+#include "rewrite-includes7.h"
|
|
+#include "rewrite-includes7.h"
|
|
+#include "rewrite-includes8.h" // no trailing \n in file
|
|
+#include "rewrite-includes9.h" // empty file
|
|
+// ENDCOMPARE
|
|
+
|
|
+// CHECK: {{^}}// STARTCOMPARE{{$}}
|
|
+// CHECK-NEXT: {{^}}#define A(a,b) a ## b{{$}}
|
|
+// CHECK-NEXT: {{^}}A(1,2){{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes1.h"{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes1.h" 1{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#pragma clang system_header{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes1.h" 3{{$}}
|
|
+// CHECK-NEXT: {{^}}included_line1{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes2.h"{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes2.h" 1 3{{$}}
|
|
+// CHECK-NEXT: {{^}}included_line2{{$}}
|
|
+// CHECK-NEXT: {{^}}# 4 "{{.*}}/Inputs/rewrite-includes1.h" 2 3{{$}}
|
|
+// CHECK-NEXT: {{^}}# 7 "{{.*}}rewrite-includes.c" 2{{$}}
|
|
+// CHECK-NEXT: {{^}}#ifdef FIRST{{$}}
|
|
+// CHECK-NEXT: {{^}}#define HEADER "rewrite-includes3.h"{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include HEADER{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes3.h" 1{{$}}
|
|
+// CHECK-NEXT: {{^}}included_line3{{$}}
|
|
+// CHECK-NEXT: {{^}}# 10 "{{.*}}rewrite-includes.c" 2{{$}}
|
|
+// CHECK-NEXT: {{^}}#else{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes4.h"{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 12 "{{.*}}rewrite-includes.c"{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#/**/include /**/ "rewrite-includes5.h" /**/ {{\\}}{{$}}
|
|
+// CHECK-NEXT: {{^}} {{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes5.h" 1{{$}}
|
|
+// CHECK-NEXT: {{^}}included_line5{{$}}
|
|
+// CHECK-NEXT: {{^}}# 15 "{{.*}}rewrite-includes.c" 2{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes6.h" // comment{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes6.h" 1{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#pragma once{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes6.h"{{$}}
|
|
+// CHECK-NEXT: {{^}}included_line6{{$}}
|
|
+// CHECK-NEXT: {{^}}# 16 "{{.*}}rewrite-includes.c" 2{{$}}
|
|
+// CHECK-NEXT: {{^}} {{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes6.h" /* comment{{$}}
|
|
+// CHECK-NEXT: {{^}} continues */{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 19 "{{.*}}rewrite-includes.c"{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes7.h"{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes7.h" 1{{$}}
|
|
+// CHECK-NEXT: {{^}}#ifndef REWRITE_INCLUDES7_H{{$}}
|
|
+// CHECK-NEXT: {{^}}#define REWRITE_INCLUDES7_H{{$}}
|
|
+// CHECK-NEXT: {{^}}included_line7{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif{{$}}
|
|
+// CHECK-NEXT: {{^}}# 20 "{{.*}}rewrite-includes.c" 2{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes7.h"{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 21 "{{.*}}rewrite-includes.c"{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes8.h" // no trailing \n in file{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes8.h" 1{{$}}
|
|
+// CHECK-NEXT: {{^}}included_line8{{$}}
|
|
+// CHECK-NEXT: {{^}}# 22 "{{.*}}rewrite-includes.c" 2{{$}}
|
|
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}#include "rewrite-includes9.h" // empty file{{$}}
|
|
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
|
|
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes9.h" 1{{$}}
|
|
+// CHECK-NEXT: {{^}}# 23 "{{.*}}rewrite-includes.c" 2{{$}}
|
|
+// CHECK-NEXT: {{^}}// ENDCOMPARE{{$}}
|