diff --git tools/clang/include/clang/Driver/CC1Options.td tools/clang/include/clang/Driver/CC1Options.td index 83c988a..b278082 100644 --- tools/clang/include/clang/Driver/CC1Options.td +++ tools/clang/include/clang/Driver/CC1Options.td @@ -452,6 +452,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 tools/clang/include/clang/Frontend/FrontendOptions.h tools/clang/include/clang/Frontend/FrontendOptions.h index 888388c..78d3adf 100644 --- tools/clang/include/clang/Frontend/FrontendOptions.h +++ tools/clang/include/clang/Frontend/FrontendOptions.h @@ -43,6 +43,7 @@ namespace frontend { PrintPreamble, ///< Print the "preamble" of the input file PrintPreprocessedInput, ///< -E mode. 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 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 { /// 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..9704fe3 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,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 tools/clang/lib/Frontend/CompilerInvocation.cpp tools/clang/lib/Frontend/CompilerInvocation.cpp index 4c5b063..4e46973 100644 --- tools/clang/lib/Frontend/CompilerInvocation.cpp +++ tools/clang/lib/Frontend/CompilerInvocation.cpp @@ -430,6 +430,7 @@ static const char *getActionName(frontend::ActionKind Kind) { case frontend::PrintPreamble: return "-print-preamble"; case frontend::PrintPreprocessedInput: return "-E"; 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"; @@ -1370,6 +1371,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::PrintPreprocessedInput; 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 tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 07d2b8d..49d0bb8 100644 --- tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -73,6 +73,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { case PrintPreamble: return new PrintPreambleAction(); case PrintPreprocessedInput: return new PrintPreprocessedAction(); 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 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..54e09c5 --- /dev/null +++ tools/clang/lib/Rewrite/RewriteIncludes.cpp @@ -0,0 +1,542 @@ +//===--- 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, public CommentHandler { + /// 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 FileChangeMap; + FileChangeMap FileChanges; + unsigned LastInsertedFileChange; + /// From HandleComment() callback, two raw-encoded SourceLocation's, + /// will be sorted by the location. + typedef std::vector< std::pair > CommentRangeVector; + CommentRangeVector CommentRanges; + bool KeepAllComments; + /// Keep comments in lines which are not only comments (such comments may be + /// visible in error/warning messages). + bool KeepCommentsInCodeLines; + 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); + virtual bool HandleComment(Preprocessor &PP, SourceRange Comment); +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 RawLex; + // file position from which the content has not yet been copied to the output + unsigned NextToWrite; + // In case of KeepCommentsInCodeLines, this is actually the position from + // which the content has not yet been written, as in that mode the last line + // is output only if it's known there is some non-whitespace content in it. + unsigned NextToWriteLine; + // the next CommentRange item that processed file contents contain (i.e. + // the current source location is >= NextToWrite's source location) + unsigned NextCommentRange; + 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); + void OutputContentStripComments(unsigned WriteEnd, bool RemoveAll); + 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) + , KeepAllComments(false) + , KeepCommentsInCodeLines(false) { + // If we're in microsoft mode, use normal #line instead of line markers. + UseLineDirective = PP.getLangOpts().MicrosoftExt; + // TODO: This is interpreting the -C and -CC options a bit differently than + // plain -E does, either document it or create new options. + if (Opts.ShowMacroComments) + KeepAllComments = true; + else if (Opts.ShowComments) + KeepCommentsInCodeLines = true; +} + +/// 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. "" 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; +} + +bool IncludeRewriter::HandleComment(Preprocessor &, SourceRange Comment) { + CommentRanges.push_back( std::make_pair(Comment.getBegin().getRawEncoding(), + Comment.getEnd().getRawEncoding())); + return false; +} + +static bool CompareCommentRanges( const std::pair& left, + const std::pair& right ) { + return left.first < right.first; +} + +void IncludeRewriter::Process() { +// Sort comment ranges for easy finding of the next one from a given location. + std::sort(CommentRanges.begin(), CommentRanges.end(), CompareCommentRanges); + 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) + , NextToWriteLine(0) + , NextCommentRange(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); + + 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(memchr(Pos + 1, '\n', End - Pos - 1)))) + ++Lines; + return Lines; +} + +// Used by OutputContentUpTo() to output content depending on comment +// removing options (either all comments are removed if RemoveAll), +// or only comments on lines that have other non-whitespace). +void IncludeRewriter::File::OutputContentStripComments(unsigned WriteEnd, + bool RemoveAll) { + unsigned LocalNextToWrite = NextToWrite; + // Only in !RemoveAll mode, true means there's something on the line + // besides whitespace and comments, so the comments should be kept. + bool LineKeepComment = false; + // Only in !RemoveAll mode, number of \n's in all comments + // from NextToWriteLine (in case they can be removed and just replaced + // with this number of \n's). + unsigned NewLinesInComments = 0; + for (;;) { + while (NextCommentRange < Rewriter->CommentRanges.size() + && Rewriter->CommentRanges[NextCommentRange].first + < LocalNextToWrite + StartLocation.getRawEncoding()) + ++NextCommentRange; + // WriteEnd cannot be in the middle of a comment. + assert( !(NextCommentRange < Rewriter->CommentRanges.size() + && Rewriter->CommentRanges[NextCommentRange].first + <= StartLocation.getRawEncoding() + WriteEnd - 1 + && Rewriter->CommentRanges[NextCommentRange].second + > StartLocation.getRawEncoding() + WriteEnd)); + if (NextCommentRange >= Rewriter->CommentRanges.size() + || StartLocation.getRawEncoding() + WriteEnd - 1 + < Rewriter->CommentRanges[NextCommentRange].first) { + // no further comment up to WriteEnd, write out all what we have + if (RemoveAll) { + OS.write(FromFile->getBufferStart() + LocalNextToWrite, + WriteEnd - LocalNextToWrite); + } else { + unsigned Pos = LocalNextToWrite; + unsigned ToWrite = WriteEnd - LocalNextToWrite; + for (unsigned int I = 0; I < ToWrite; ++I, ++Pos) { + char C = FromFile->getBufferStart()[Pos]; + // TODO This is possibly wrong for EOL different from plain \n. + if (C == '\n') { + if (LineKeepComment) + OS.write(FromFile->getBufferStart() + NextToWriteLine, + Pos - NextToWriteLine + 1); + else { + for (unsigned I = 0; I < NewLinesInComments; ++I) + OS << EOL; + OS << EOL; + } + NextToWriteLine = Pos + 1; + LineKeepComment = false; + NewLinesInComments = 0; + } + if (!isspace(C)) + LineKeepComment = true; + } + if (WriteEnd == SM.getFileIDSize(FileId)) { + // End of file, write out the rest if needed (there's no comment + // in it, so just write it out). + OS.write(FromFile->getBufferStart() + NextToWriteLine, + WriteEnd - NextToWriteLine); + NextToWriteLine = WriteEnd; + } + } + return; + } + const unsigned CommentStart = Rewriter->CommentRanges + [NextCommentRange].first - StartLocation.getRawEncoding(); + const unsigned CommentEnd = Rewriter->CommentRanges + [NextCommentRange].second - StartLocation.getRawEncoding();//first after + // data up to the next comment + unsigned ToWrite = CommentStart - LocalNextToWrite; + if (RemoveAll) { + if (ToWrite > 0) + OS.write(FromFile->getBufferStart() + LocalNextToWrite, ToWrite); + // keep \n's and trailing \'s from the comment + unsigned Pos = CommentStart; + for (unsigned int I = 0; I < CommentEnd - CommentStart; ++I, ++Pos) { + char C = FromFile->getBufferStart()[Pos]; + // TODO This is possibly wrong for EOL different from plain \n. + if (C == '\n') + OS << EOL; + else if (C == '\\' && I + 1 < CommentEnd - CommentStart + && FromFile->getBufferStart()[Pos + 1] == '\n') { + OS << '\\'; + } + } + } else { + // Check if there's any \n, if yes, check if the line before it + // should be output (including comments) or whether it has only + // whitespace and comments, in which case output only \n for it. + unsigned Pos = LocalNextToWrite; + for (unsigned int I = 0; I < ToWrite; ++I, ++Pos) { + char C = FromFile->getBufferStart()[Pos]; + // TODO This is possibly wrong for EOL different from plain \n. + if (C == '\n') { + if (LineKeepComment) + OS.write(FromFile->getBufferStart() + NextToWriteLine, + Pos - NextToWriteLine + 1); + else { + for (unsigned I = 0; I < NewLinesInComments; ++I) + OS << EOL; + OS << EOL; + } + NextToWriteLine = Pos + 1; + LineKeepComment = false; + NewLinesInComments = 0; + } + if (!isspace(C)) + LineKeepComment = true; + } + // Process the comment - count the number of \n's in it, in case + // it can be removed. + // TODO Ideally, if there is a multiline comment, all lines inside + // the comment should be replaced with empty lines, to further reduce + // the size of the resulting output, even if the first and/or last + // line of the comment contain also other non-whitespace and should + // therefore be kept. Currently such a comment is kept as a whole. + // However in practice multiline comments, especially large ones + // (doxygen comments, file headers) usually can be removed as a whole, + // so this probably would not make a big difference. + for (unsigned int I = 0; I < CommentEnd - CommentStart; ++I, ++Pos) { + char C = FromFile->getBufferStart()[Pos]; + if (C == '\n') { + ++NewLinesInComments; + } else if (C == '\\' && I + 1 < CommentEnd - CommentStart + && FromFile->getBufferStart()[Pos + 1] == '\n') { + // Do not remove lines in multiline comments that end with \, + // as that could be a body of a macro. + // TODO This is possibly wrong with EOL different from plain \n. + LineKeepComment = true; + } + } + } + // and move after the comment + LocalNextToWrite = CommentEnd; + } +} + +/// Copies next not yet written file content up (and not including) to writeEnd. +void IncludeRewriter::File::OutputContentUpTo(unsigned WriteEnd, + bool EnsureNewline) { + if (WriteEnd > NextToWrite) { + if (Rewriter->KeepAllComments) + OS.write(FromFile->getBufferStart() + NextToWrite, WriteEnd - NextToWrite); + else if (Rewriter->KeepCommentsInCodeLines) + OutputContentStripComments(WriteEnd, false); + else // Remove all comments. + OutputContentStripComments(WriteEnd, true); + // 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 -rewrite-includes */" << EOL; + OutputContentUpTo(SM.getFileOffset(DirectiveToken.getLocation()) + + DirectiveToken.getLength()); + OS << "#endif /* expanded by -rewrite-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 -rewrite-includes mode. +void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS, + const PreprocessorOutputOptions &Opts) { + IncludeRewriter* Rewrite = new IncludeRewriter(PP, *OS, Opts); + PP.addPPCallbacks(Rewrite); + PP.AddCommentHandler(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/make_patch tools/clang/make_patch index d42af26..61a263d 100755 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..52be495 --- /dev/null +++ tools/clang/test/Frontend/rewrite-includes.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -verify -rewrite-includes -DFIRST -I %S/Inputs %s -CC -o - | FileCheck -check-prefix=CHECK-C2 -strict-whitespace %s.check +// RUN: %clang_cc1 -verify -rewrite-includes -DFIRST -I %S/Inputs %s -C -o - | FileCheck -check-prefix=CHECK-C1 -strict-whitespace %s.check +// RUN: %clang_cc1 -verify -rewrite-includes -DFIRST -I %S/Inputs %s -o - | FileCheck -check-prefix=CHECK-C0 -strict-whitespace %s.check + +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 + + // comment + /* + multiline + comment + */ +#define MACRO \ + /*comment*/ \ + /*multi \ + line*/ \ + +MACRO +/* +\ +comment +*/ +foo /* multiline +with code +prepended */ +/* multiline +with code +appended */ bar +#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 diff --git tools/clang/test/Frontend/rewrite-includes.c.check tools/clang/test/Frontend/rewrite-includes.c.check new file mode 100644 index 0000000..7e920a5 --- /dev/null +++ tools/clang/test/Frontend/rewrite-includes.c.check @@ -0,0 +1,308 @@ +Modes that remove comments would also remove these check lines, so keep them in a separate file. + +Note that FileCheck does not seem to have a way to check for an empty line, +so there is just a comment, the next non-empty line is not -NEXT but a plain check line +and the line correctness is checked by same later # directive check. + +This one checks -CC, i.e. all comments are kept. It's the primary mode for the test. + +// CHECK-C2: {{^}}STARTCOMPARE{{$}} +// CHECK-C2-NEXT: {{^}}#define A(a,b) a ## b{{$}} +// CHECK-C2-NEXT: {{^}}A(1,2){{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes1.h"{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes1.h" 1{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#pragma clang system_header{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes1.h" 3{{$}} +// CHECK-C2-NEXT: {{^}}included_line1{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes2.h"{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes2.h" 1 3{{$}} +// CHECK-C2-NEXT: {{^}}included_line2{{$}} +// CHECK-C2-NEXT: {{^}}# 4 "{{.*}}/Inputs/rewrite-includes1.h" 2 3{{$}} +// CHECK-C2-NEXT: {{^}}# 9 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C2-NEXT: {{^}}#ifdef FIRST{{$}} +// CHECK-C2-NEXT: {{^}}#define HEADER "rewrite-includes3.h"{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include HEADER{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes3.h" 1{{$}} +// CHECK-C2-NEXT: {{^}}included_line3{{$}} +// CHECK-C2-NEXT: {{^}}# 12 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C2-NEXT: {{^}}#else{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes4.h"{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 14 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C2-NEXT: {{^}}#endif{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#/**/include /**/ "rewrite-includes5.h" /**/ {{\\}}{{$}} +// CHECK-C2-NEXT: {{^}} {{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes5.h" 1{{$}} +// CHECK-C2-NEXT: {{^}}included_line5{{$}} +// CHECK-C2-NEXT: {{^}}# 17 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes6.h" // comment{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes6.h" 1{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#pragma once{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes6.h"{{$}} +// CHECK-C2-NEXT: {{^}}included_line6{{$}} +// CHECK-C2-NEXT: {{^}}# 18 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C2-NEXT: {{^}} {{$}} +// CHECK-C2-NEXT: {{^}} // comment{{$}} +// CHECK-C2-NEXT: {{^}} /*{{$}} +// CHECK-C2-NEXT: {{^}} multiline{{$}} +// CHECK-C2-NEXT: {{^}} comment{{$}} +// CHECK-C2-NEXT: {{^}} */{{$}} +// CHECK-C2-NEXT: {{^}}#define MACRO \{{$}} +// CHECK-C2-NEXT: {{^}} /*comment*/ \{{$}} +// CHECK-C2-NEXT: {{^}} /*multi \{{$}} +// CHECK-C2-NEXT: {{^}} line*/ \{{$}} +// CHECK-C2-NEXT: {{^}} {{$}} +// CHECK-C2-NEXT: {{^}}MACRO{{$}} +// CHECK-C2-NEXT: {{^}}/*{{$}} +// CHECK-C2-NEXT: {{^}}\{{$}} +// CHECK-C2-NEXT: {{^}}comment{{$}} +// CHECK-C2-NEXT: {{^}}*/{{$}} +// CHECK-C2-NEXT: {{^}}foo /* multiline{{$}} +// CHECK-C2-NEXT: {{^}}with code{{$}} +// CHECK-C2-NEXT: {{^}}prepended */{{$}} +// CHECK-C2-NEXT: {{^}}/* multiline{{$}} +// CHECK-C2-NEXT: {{^}}with code{{$}} +// CHECK-C2-NEXT: {{^}}appended */ bar{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes6.h" /* comment{{$}} +// CHECK-C2-NEXT: {{^}} continues */{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 42 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes7.h" 1{{$}} +// CHECK-C2-NEXT: {{^}}#ifndef REWRITE_INCLUDES7_H{{$}} +// CHECK-C2-NEXT: {{^}}#define REWRITE_INCLUDES7_H{{$}} +// CHECK-C2-NEXT: {{^}}included_line7{{$}} +// CHECK-C2-NEXT: {{^}}#endif{{$}} +// CHECK-C2-NEXT: {{^}}# 43 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 44 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes8.h" // no trailing \n in file{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes8.h" 1{{$}} +// CHECK-C2-NEXT: {{^}}included_line8{{$}} +// CHECK-C2-NEXT: {{^}}# 45 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C2-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}#include "rewrite-includes9.h" // empty file{{$}} +// CHECK-C2-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C2-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes9.h" 1{{$}} +// CHECK-C2-NEXT: {{^}}# 46 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C2-NEXT: {{^}}ENDCOMPARE{{$}} + +This one checks -C, i.e. only comments on lines with other non-whitespace are kept. +Note that lines containing only whitespace are not preserved exactly and are converted to empty lines. + +// CHECK-C1: {{^}}STARTCOMPARE{{$}} +// CHECK-C1-NEXT: {{^}}#define A(a,b) a ## b{{$}} +// CHECK-C1-NEXT: {{^}}A(1,2){{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes1.h"{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes1.h" 1{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#pragma clang system_header{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes1.h" 3{{$}} +// CHECK-C1-NEXT: {{^}}included_line1{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes2.h"{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes2.h" 1 3{{$}} +// CHECK-C1-NEXT: {{^}}included_line2{{$}} +// CHECK-C1-NEXT: {{^}}# 4 "{{.*}}/Inputs/rewrite-includes1.h" 2 3{{$}} +// CHECK-C1-NEXT: {{^}}# 9 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C1-NEXT: {{^}}#ifdef FIRST{{$}} +// CHECK-C1-NEXT: {{^}}#define HEADER "rewrite-includes3.h"{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include HEADER{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes3.h" 1{{$}} +// CHECK-C1-NEXT: {{^}}included_line3{{$}} +// CHECK-C1-NEXT: {{^}}# 12 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C1-NEXT: {{^}}#else{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes4.h"{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 14 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C1-NEXT: {{^}}#endif{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#/**/include /**/ "rewrite-includes5.h" /**/ {{\\}}{{$}} +// here's an empty line +// CHECK-C1: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes5.h" 1{{$}} +// CHECK-C1-NEXT: {{^}}included_line5{{$}} +// CHECK-C1-NEXT: {{^}}# 17 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes6.h" // comment{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes6.h" 1{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#pragma once{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes6.h"{{$}} +// CHECK-C1-NEXT: {{^}}included_line6{{$}} +// CHECK-C1-NEXT: {{^}}# 18 "{{.*}}rewrite-includes.c" 2{{$}} +// here are 6 empty lines +// CHECK-C1: {{^}}#define MACRO \{{$}} +// CHECK-C1-NEXT: {{^}} /*comment*/ \{{$}} +// CHECK-C1-NEXT: {{^}} /*multi \{{$}} +// CHECK-C1-NEXT: {{^}} line*/ \{{$}} +// here's an empty line +// CHECK-C1: {{^}}MACRO{{$}} +// CHECK-C1-NEXT: {{^}}/*{{$}} +// CHECK-C1-NEXT: {{^}}\{{$}} +// CHECK-C1-NEXT: {{^}}comment{{$}} +// CHECK-C1-NEXT: {{^}}*/{{$}} +// CHECK-C1-NEXT: {{^}}foo /* multiline{{$}} +// CHECK-C1-NEXT: {{^}}with code{{$}} +// CHECK-C1-NEXT: {{^}}prepended */{{$}} +// CHECK-C1-NEXT: {{^}}/* multiline{{$}} +// CHECK-C1-NEXT: {{^}}with code{{$}} +// CHECK-C1-NEXT: {{^}}appended */ bar{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes6.h" /* comment{{$}} +// CHECK-C1-NEXT: {{^}} continues */{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 42 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes7.h" 1{{$}} +// CHECK-C1-NEXT: {{^}}#ifndef REWRITE_INCLUDES7_H{{$}} +// CHECK-C1-NEXT: {{^}}#define REWRITE_INCLUDES7_H{{$}} +// CHECK-C1-NEXT: {{^}}included_line7{{$}} +// CHECK-C1-NEXT: {{^}}#endif{{$}} +// CHECK-C1-NEXT: {{^}}# 43 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 44 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes8.h" // no trailing \n in file{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes8.h" 1{{$}} +// CHECK-C1-NEXT: {{^}}included_line8{{$}} +// CHECK-C1-NEXT: {{^}}# 45 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C1-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}#include "rewrite-includes9.h" // empty file{{$}} +// CHECK-C1-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C1-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes9.h" 1{{$}} +// CHECK-C1-NEXT: {{^}}# 46 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C1-NEXT: {{^}}ENDCOMPARE{{$}} + +This one checks without -C or -CC, i.e. comments removed. + +// CHECK-C0: {{^}}STARTCOMPARE{{$}} +// CHECK-C0-NEXT: {{^}}#define A(a,b) a ## b{{$}} +// CHECK-C0-NEXT: {{^}}A(1,2){{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes1.h"{{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes1.h" 1{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#pragma clang system_header{{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes1.h" 3{{$}} +// CHECK-C0-NEXT: {{^}}included_line1{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes2.h"{{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes2.h" 1 3{{$}} +// CHECK-C0-NEXT: {{^}}included_line2{{$}} +// CHECK-C0-NEXT: {{^}}# 4 "{{.*}}/Inputs/rewrite-includes1.h" 2 3{{$}} +// CHECK-C0-NEXT: {{^}}# 9 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C0-NEXT: {{^}}#ifdef FIRST{{$}} +// CHECK-C0-NEXT: {{^}}#define HEADER "rewrite-includes3.h"{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include HEADER{{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes3.h" 1{{$}} +// CHECK-C0-NEXT: {{^}}included_line3{{$}} +// CHECK-C0-NEXT: {{^}}# 12 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C0-NEXT: {{^}}#else{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes4.h"{{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 14 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C0-NEXT: {{^}}#endif{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes5.h" {{\\}}{{$}} +// CHECK-C0-NEXT: {{^}} {{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes5.h" 1{{$}} +// CHECK-C0-NEXT: {{^}}included_line5{{$}} +// CHECK-C0-NEXT: {{^}}# 17 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes6.h" {{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes6.h" 1{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#pragma once{{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 2 "{{.*}}/Inputs/rewrite-includes6.h"{{$}} +// CHECK-C0-NEXT: {{^}}included_line6{{$}} +// CHECK-C0-NEXT: {{^}}# 18 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C0-NEXT: {{^}} {{$}} +// here are 5 empty lines +// CHECK-C0: {{^}}#define MACRO \{{$}} +// CHECK-C0-NEXT: {{^}} \{{$}} +// CHECK-C0-NEXT: {{^}} \{{$}} +// CHECK-C0-NEXT: {{^}} \{{$}} +// here's an empty line +// CHECK-C0: {{^}}MACRO{{$}} +// here's an empty line +// CHECK-C0: {{^}}\{{$}} +// here are 2 empty lines +// CHECK-C0: {{^}}foo {{$}} +// here are 4 empty lines +// CHECK-C0: {{^}} bar{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes6.h" {{$}} +// here's an empty line +// CHECK-C0: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 42 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes7.h" 1{{$}} +// CHECK-C0-NEXT: {{^}}#ifndef REWRITE_INCLUDES7_H{{$}} +// CHECK-C0-NEXT: {{^}}#define REWRITE_INCLUDES7_H{{$}} +// CHECK-C0-NEXT: {{^}}included_line7{{$}} +// CHECK-C0-NEXT: {{^}}#endif{{$}} +// CHECK-C0-NEXT: {{^}}# 43 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 44 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes8.h" {{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes8.h" 1{{$}} +// CHECK-C0-NEXT: {{^}}included_line8{{$}} +// CHECK-C0-NEXT: {{^}}# 45 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C0-NEXT: {{^}}#if 0 /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}#include "rewrite-includes9.h" {{$}} +// CHECK-C0-NEXT: {{^}}#endif /* expanded by -rewrite-includes */{{$}} +// CHECK-C0-NEXT: {{^}}# 1 "{{.*}}/Inputs/rewrite-includes9.h" 1{{$}} +// CHECK-C0-NEXT: {{^}}# 46 "{{.*}}rewrite-includes.c" 2{{$}} +// CHECK-C0-NEXT: {{^}}ENDCOMPARE{{$}}