On Tuesday 27 of March 2012, Matt Beaumont-Gay wrote:
> On Fri, Mar 23, 2012 at 02:08, Lubos Lunak <[email protected]> wrote:
> > On Friday 23 of March 2012, Matt Beaumont-Gay wrote:
> >> Prefer std::vector -- no reason to use std::list here.
> >
> > I've meanwhile confirmed that it should be actually std::map for
> > noticeably better performance.
>
> Does a DenseMap (#include "llvm/ADT/DenseMap.h") of SourceLocation to
> pair<FileID, CharacteristicKind> improve performance any further?
No, because it doesn't compile. Even if I use it properly, the difference is
neglibible.
> > Clang does not even bother to perform repeated inclusion of the same
> > file if it somehow detects it's unnecessary, but the avoided
> > FileChanged() callback would mean -rewrite-includes would otherwise leave
> > some #include directives in the result that would be performed the next
> > time.
>
> Can you use the FileSkipped() callback to make this any more efficient?
No. If you are asking about the quoted part above, then it's not about
efficiency, it's about correct/incorrect. If it was a question in general,
then it's more efficient (and probably also correct, given the action is
supposed to rewrite includes) to just comment out everything instead of
figuring out exactly what needs it or not.
> A few new comments, in addition to the general style issues which are
> still unaddressed (doxygen, whitespace, etc):
I have already converted function/type comments into doxygen comments, and
there's no point in converting code comments. If you meant to say something
else with "Doxygen comments throughout", you didn't actually say it.
Updated patch attached.
--
Lubos Lunak
[email protected]
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index 18caca6..b27a717 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -455,6 +455,8 @@ def rewrite_objc : Flag<"-rewrite-objc">,
HelpText<"Rewrite ObjC into C (code rewriter example)">;
def rewrite_macros : Flag<"-rewrite-macros">,
HelpText<"Expand macros without full preprocessing">;
+def rewrite_includes : Flag<"-rewrite-includes">,
+ HelpText<"Expand includes without full preprocessing">;
def migrate : Flag<"-migrate">,
HelpText<"Migrate source code">;
}
diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h
index 49a9ec1..02a0f66 100644
--- a/include/clang/Frontend/FrontendOptions.h
+++ b/include/clang/Frontend/FrontendOptions.h
@@ -44,6 +44,7 @@ namespace frontend {
PrintPreprocessedInput, ///< -E mode.
PubnamesDump, ///< Print all of the "public" names in the source.
RewriteMacros, ///< Expand macros but not #includes.
+ RewriteIncludes, ///< Expand #includes but not macros.
RewriteObjC, ///< ObjC->C Rewriter.
RewriteTest, ///< Rewriter playground
RunAnalysis, ///< Run one or more source code analyses.
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index 591a449..4b8e1c5 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/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;
@@ -631,6 +638,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 a/include/clang/Rewrite/FrontendActions.h b/include/clang/Rewrite/FrontendActions.h
index 6e9ecac..ea876d9 100644
--- a/include/clang/Rewrite/FrontendActions.h
+++ b/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 a/include/clang/Rewrite/Rewriters.h b/include/clang/Rewrite/Rewriters.h
index 203b9bc..9704fe3 100644
--- a/include/clang/Rewrite/Rewriters.h
+++ b/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,9 @@ void RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS);
/// DoRewriteTest - A simple test for the TokenRewriter class.
void DoRewriteTest(Preprocessor &PP, raw_ostream *OS);
+/// RewriteIncludesInInput - Implement -rewrite-includes mode.
+void RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS, const PreprocessorOutputOptions &Opts);
+
} // end namespace clang
#endif
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 6e36242..fe00a63 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -431,6 +431,7 @@ static const char *getActionName(frontend::ActionKind Kind) {
case frontend::PrintPreprocessedInput: return "-E";
case frontend::PubnamesDump: return "-pubnames-dump";
case frontend::RewriteMacros: return "-rewrite-macros";
+ case frontend::RewriteIncludes: return "-rewrite-includes";
case frontend::RewriteObjC: return "-rewrite-objc";
case frontend::RewriteTest: return "-rewrite-test";
case frontend::RunAnalysis: return "-analyze";
@@ -1371,6 +1372,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
Opts.ProgramAction = frontend::PubnamesDump; break;
case OPT_rewrite_macros:
Opts.ProgramAction = frontend::RewriteMacros; break;
+ case OPT_rewrite_includes:
+ Opts.ProgramAction = frontend::RewriteIncludes; break;
case OPT_rewrite_objc:
Opts.ProgramAction = frontend::RewriteObjC; break;
case OPT_rewrite_test:
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 2066505..e82cd2f 100644
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -74,6 +74,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) {
case PrintPreprocessedInput: return new PrintPreprocessedAction();
case PubnamesDump: return new PubnamesDumpAction();
case RewriteMacros: return new RewriteMacrosAction();
+ case RewriteIncludes: return new RewriteIncludesAction();
case RewriteObjC: return new RewriteObjCAction();
case RewriteTest: return new RewriteTestAction();
case RunAnalysis: return new ento::AnalysisAction();
diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp
index a49ab04..65d96d8 100644
--- a/lib/Lex/Lexer.cpp
+++ b/lib/Lex/Lexer.cpp
@@ -2020,7 +2020,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
@@ -2622,7 +2622,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 a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp
index 625a204..29a8614 100644
--- a/lib/Lex/PPDirectives.cpp
+++ b/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 a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp
index f7f63ee..e8bec85 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -134,6 +134,7 @@ void Preprocessor::Initialize(const TargetInfo &Target) {
// Macro expansion is enabled.
DisableMacroExpansion = false;
+ MacroExpansionInDirectivesOverride = false;
InMacroArgs = false;
NumCachedTokenLexers = 0;
diff --git a/lib/Rewrite/CMakeLists.txt b/lib/Rewrite/CMakeLists.txt
index 2a05040..8070ba2 100644
--- a/lib/Rewrite/CMakeLists.txt
+++ b/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 a/lib/Rewrite/FrontendActions.cpp b/lib/Rewrite/FrontendActions.cpp
index 1753325..e462671 100644
--- a/lib/Rewrite/FrontendActions.cpp
+++ b/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 a/lib/Rewrite/RewriteIncludes.cpp b/lib/Rewrite/RewriteIncludes.cpp
new file mode 100644
index 0000000..fe1f077
--- /dev/null
+++ b/lib/Rewrite/RewriteIncludes.cpp
@@ -0,0 +1,333 @@
+//===--- 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 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;
+public:
+ IncludeRewriter(Preprocessor &PP, raw_ostream &OS, bool LineMarkers);
+ bool Process(FileID FileId, SrcMgr::CharacteristicKind Type);
+private:
+ 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);
+ void WriteLineInfo(const char* Filename, int Line,
+ SrcMgr::CharacteristicKind Type,
+ const char* EOL, StringRef Extra = StringRef());
+ void OutputContentUpTo(unsigned WriteEnd, unsigned& NextToWrite,
+ int& Lines, const char* EOL,
+ const MemoryBuffer* FromFile,
+ bool EnsureNewline = false);
+ void CommentOutDirective(Lexer& DirectivesLex, const Token& StartToken,
+ unsigned& NextToWrite, int& Lines,
+ const char* EOL, const MemoryBuffer* FromFile);
+ const FileChange* FindFileChangeLocation(SourceLocation Loc) const;
+ StringRef NextIdentifierName(Lexer& RawLex, Token& RawToken);
+};
+
+} // end anonymous namespace
+
+::IncludeRewriter::IncludeRewriter(Preprocessor &pp, raw_ostream &os,
+ bool lineMarkers)
+ : PP(pp), SM(PP.getSourceManager()), OS(os),
+ DisableLineMarkers(lineMarkers), LastInsertedFileChange(0) {
+ // If we're in microsoft mode, use normal #line instead of line markers.
+ UseLineDirective = PP.getLangOpts().MicrosoftExt;
+}
+
+void IncludeRewriter::WriteLineInfo(const char* Filename, int Line,
+ SrcMgr::CharacteristicKind Type,
+ const char* EOL, StringRef Extra) {
+ // Emit #line directives or GNU line markers depending on what mode we're in.
+ if (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;
+}
+
+/// 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;
+}
+
+inline unsigned 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;
+}
+
+static const char* DetectEOL(const MemoryBuffer* FromFile) {
+ // 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)
+ return "\n";
+ if (Pos+1 < FromFile->getBufferEnd() && *(Pos+1) == '\r')
+ return "\n\r";
+ if (Pos-1 >= FromFile->getBufferStart() && *(Pos+1) == '\r')
+ return "\r\n";
+ return "\n";
+}
+
+/// Copies next yet written file content up (and not including writeEnd).
+void IncludeRewriter::OutputContentUpTo(unsigned WriteEnd,
+ unsigned& NextToWrite,
+ int& Lines, const char* EOL,
+ const MemoryBuffer* FromFile,
+ bool EnsureNewline) {
+ if (WriteEnd > NextToWrite) {
+ OS.write(FromFile->getBufferStart() + NextToWrite, WriteEnd - NextToWrite);
+ // count lines manually, it's faster than getPresumedLoc()
+ Lines += 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::CommentOutDirective(Lexer& DirectiveLex,
+ const Token& StartToken,
+ unsigned& NextToWrite, int& Lines,
+ const char* EOL,
+ const MemoryBuffer* FromFile) {
+ OutputContentUpTo(SM.getFileOffset(StartToken.getLocation()), NextToWrite,
+ Lines, EOL, FromFile);
+ Token DirectiveToken;
+ do {
+ DirectiveLex.LexFromRawLexer(DirectiveToken);
+ } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof));
+ OS << "#if 0 /* expanded by -rewrite-includes */" << EOL;
+ OutputContentUpTo(SM.getFileOffset(DirectiveToken.getLocation())
+ + DirectiveToken.getLength(), NextToWrite, Lines, EOL, FromFile);
+ OS << "#endif /* expanded by -rewrite-includes */" << EOL;
+}
+
+StringRef IncludeRewriter::NextIdentifierName(Lexer& RawLex, 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();
+}
+
+bool IncludeRewriter::Process(FileID FileId, SrcMgr::CharacteristicKind Type)
+{
+ bool Invalid;
+ const char* FileName = SM.getBufferName(SM.getLocForStartOfFile(FileId),
+ &Invalid);
+ if (Invalid)
+ return false;
+
+ // Use a raw lexer to analyze the input file, incrementally copying parts of
+ // it and including content of included files recursively.
+ const MemoryBuffer *FromFile = SM.getBuffer(FileId);
+ Lexer RawLex(FileId, FromFile, PP.getSourceManager(), PP.getLangOpts());
+ RawLex.SetCommentRetentionState(false);
+
+ const char* EOL = DetectEOL(FromFile);
+
+ WriteLineInfo(FileName, 1, Type, EOL, " 1");
+
+ if (SM.getFileIDSize(FileId) == 0)
+ return true;
+
+ // file position from which the content has not yet been copied to the output
+ unsigned NextToWrite = 0;
+ int Lines = 1; // current input file line number
+
+ 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(RawLex, HashToken, NextToWrite, Lines, EOL,
+ FromFile);
+ // fix up lineinfo, commenting out has added lines
+ bool NeedFixup = true;
+ if (const FileChange* Change = FindFileChangeLocation(
+ HashToken.getLocation())) {
+ // now include and recursively process the file
+ if(Process(Change->Id, Change->Type)) {
+ // and set lineinfo back to this file, if the nested one was
+ // actually included
+ WriteLineInfo(FileName, Lines, Type, EOL, " 2");
+ NeedFixup = false;
+ }
+ }
+ if(NeedFixup)
+ WriteLineInfo(FileName, Lines, Type, EOL);
+ break;
+ }
+ case tok::pp_pragma: {
+ StringRef Identifier = NextIdentifierName(RawLex, RawToken);
+ if (Identifier == "clang" || Identifier == "GCC") {
+ if (NextIdentifierName(RawLex, RawToken) == "system_header") {
+ // keep the directive in, commented out
+ CommentOutDirective(RawLex, HashToken, NextToWrite, Lines,
+ EOL, FromFile);
+ // update our own type
+ Type = SM.getFileCharacteristic(RawToken.getLocation());
+ WriteLineInfo(FileName, Lines, Type, EOL);
+ }
+ } else if (Identifier == "once") {
+ // keep the directive in, commented out
+ CommentOutDirective(RawLex, HashToken, NextToWrite, Lines,
+ EOL, FromFile);
+ WriteLineInfo(FileName, Lines, Type, EOL);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ RawLex.setParsingPreprocessorDirective(false);
+ RawLex.SetCommentRetentionState(false);
+ }
+ RawLex.LexFromRawLexer(RawToken);
+ }
+ OutputContentUpTo(SM.getFileOffset(SM.getLocForEndOfFile(FileId)) + 1,
+ NextToWrite, Lines, EOL, FromFile, true);
+ return true;
+}
+
+/// RewriteIncludesInInput - Implement -rewrite-includes mode.
+void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
+ const PreprocessorOutputOptions &Opts) {
+ SourceManager &SM = PP.getSourceManager();
+ IncludeRewriter* Rewrite = new IncludeRewriter(PP, *OS,!Opts.ShowLineMarkers);
+ 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(SM.getMainFileID(), SrcMgr::C_User);
+ OS->flush();
+}
diff --git a/test/Frontend/Inputs/rewrite-includes1.h b/test/Frontend/Inputs/rewrite-includes1.h
new file mode 100644
index 0000000..1b6c80d
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes1.h
@@ -0,0 +1,3 @@
+#pragma clang system_header
+included_line1
+#include "rewrite-includes2.h"
diff --git a/test/Frontend/Inputs/rewrite-includes2.h b/test/Frontend/Inputs/rewrite-includes2.h
new file mode 100644
index 0000000..1114e51
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes2.h
@@ -0,0 +1 @@
+included_line2
diff --git a/test/Frontend/Inputs/rewrite-includes3.h b/test/Frontend/Inputs/rewrite-includes3.h
new file mode 100644
index 0000000..3757bc8
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes3.h
@@ -0,0 +1 @@
+included_line3
diff --git a/test/Frontend/Inputs/rewrite-includes4.h b/test/Frontend/Inputs/rewrite-includes4.h
new file mode 100644
index 0000000..b4e25d2
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes4.h
@@ -0,0 +1 @@
+included_line4
diff --git a/test/Frontend/Inputs/rewrite-includes5.h b/test/Frontend/Inputs/rewrite-includes5.h
new file mode 100644
index 0000000..934bf41
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes5.h
@@ -0,0 +1 @@
+included_line5
diff --git a/test/Frontend/Inputs/rewrite-includes6.h b/test/Frontend/Inputs/rewrite-includes6.h
new file mode 100644
index 0000000..c18e501
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes6.h
@@ -0,0 +1,2 @@
+#pragma once
+included_line6
diff --git a/test/Frontend/rewrite-includes.c b/test/Frontend/rewrite-includes.c
new file mode 100644
index 0000000..fbe901f
--- /dev/null
+++ b/test/Frontend/rewrite-includes.c
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 -verify -rewrite-includes -DFIRST -I %S/Inputs %s -o %t
+// RUN: cat %t | 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 */
+// ENDCOMPARE
+
+// CHECK: {{^}}// STARTCOMPARE{{$}}
+// CHECK-NEXT: {{^}}#define A(a,b) a ## b{{$}}
+// CHECK-NEXT: {{^}}A(1,2){{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes1.h"{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes1.h" 1{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#pragma clang system_header{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes1.h" 3{{$}}
+// CHECK-NEXT: {{^}}included_line1{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes2.h"{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-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 -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include HEADER{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-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 -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes4.h"{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 12 "{{.*}}rewrite-includes.c"{{$}}
+// CHECK-NEXT: {{^}}#endif{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#/**/include /**/ "rewrite-includes5.h" /**/ {{\\}}{{$}}
+// CHECK-NEXT: {{^}} {{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-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 -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes6.h" // comment{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes6.h" 1{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#pragma once{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-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 -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes6.h" /* comment{{$}}
+// CHECK-NEXT: {{^}} continues */{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 19 "{{.*}}rewrite-includes.c"{{$}}
+// CHECK-NEXT: {{^}}// ENDCOMPARE{{$}}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits