Index: clang/lib/Frontend/Rewrite/InclusionRewriter.cpp =================================================================== --- clang/lib/Frontend/Rewrite/InclusionRewriter.cpp +++ clang/lib/Frontend/Rewrite/InclusionRewriter.cpp @@ -49,6 +49,8 @@ std::map ModuleIncludes; /// Tracks where inclusions that enter modules (in a module build) are found. std::map ModuleEntryIncludes; + /// Tracks where #if and #elif directives get evaluated and whether to true. + std::map IfConditions; /// Used transitively for building up the FileIncludes mapping over the /// various \c PPCallbacks callbacks. SourceLocation LastInclusionLocation; @@ -78,6 +80,10 @@ StringRef SearchPath, StringRef RelativePath, const Module *Imported, SrcMgr::CharacteristicKind FileType) override; + void If(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue) override; + void Elif(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue, SourceLocation IfLoc) override; void WriteLineInfo(StringRef Filename, int Line, SrcMgr::CharacteristicKind FileType, StringRef Extra = StringRef()); @@ -89,12 +95,10 @@ void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, const MemoryBuffer &FromFile, StringRef EOL, unsigned &NextToWrite, int &Lines); - bool HandleHasInclude(FileID FileId, Lexer &RawLex, - const DirectoryLookup *Lookup, Token &Tok, - bool &FileExists); const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) const; const Module *FindModuleAtLocation(SourceLocation Loc) const; const Module *FindEnteredModule(SourceLocation Loc) const; + bool IsIfAtLocationTrue(SourceLocation Loc) const; StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); }; @@ -203,6 +207,23 @@ LastInclusionLocation = HashLoc; } +void InclusionRewriter::If(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue) { + auto P = IfConditions.insert( + std::make_pair(Loc.getRawEncoding(), ConditionValue == CVK_True)); + (void)P; + assert(P.second && "Unexpected revisitation of the same if directive"); +} + +void InclusionRewriter::Elif(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue, + SourceLocation IfLoc) { + auto P = IfConditions.insert( + std::make_pair(Loc.getRawEncoding(), ConditionValue == CVK_True)); + (void)P; + assert(P.second && "Unexpected revisitation of the same elif directive"); +} + /// Simple lookup for a SourceLocation (specifically one denoting the hash in /// an inclusion directive) in the map of inclusion information, FileChanges. const InclusionRewriter::IncludedFile * @@ -233,6 +254,13 @@ return nullptr; } +bool InclusionRewriter::IsIfAtLocationTrue(SourceLocation Loc) const { + const auto I = IfConditions.find(Loc.getRawEncoding()); + if (I != IfConditions.end()) + return I->second; + return false; +} + /// Detect the likely line ending style of \p FromFile by examining the first /// newline found within it. static StringRef DetectEOL(const MemoryBuffer &FromFile) { @@ -346,80 +374,6 @@ return StringRef(); } -// Expand __has_include and __has_include_next if possible. If there's no -// definitive answer return false. -bool InclusionRewriter::HandleHasInclude( - FileID FileId, Lexer &RawLex, const DirectoryLookup *Lookup, Token &Tok, - bool &FileExists) { - // Lex the opening paren. - RawLex.LexFromRawLexer(Tok); - if (Tok.isNot(tok::l_paren)) - return false; - - RawLex.LexFromRawLexer(Tok); - - SmallString<128> FilenameBuffer; - StringRef Filename; - // Since the raw lexer doesn't give us angle_literals we have to parse them - // ourselves. - // FIXME: What to do if the file name is a macro? - if (Tok.is(tok::less)) { - RawLex.LexFromRawLexer(Tok); - - FilenameBuffer += '<'; - do { - if (Tok.is(tok::eod)) // Sanity check. - return false; - - if (Tok.is(tok::raw_identifier)) - PP.LookUpIdentifierInfo(Tok); - - // Get the string piece. - SmallVector TmpBuffer; - bool Invalid = false; - StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid); - if (Invalid) - return false; - - FilenameBuffer += TmpName; - - RawLex.LexFromRawLexer(Tok); - } while (Tok.isNot(tok::greater)); - - FilenameBuffer += '>'; - Filename = FilenameBuffer; - } else { - if (Tok.isNot(tok::string_literal)) - return false; - - bool Invalid = false; - Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid); - if (Invalid) - return false; - } - - // Lex the closing paren. - RawLex.LexFromRawLexer(Tok); - if (Tok.isNot(tok::r_paren)) - return false; - - // Now ask HeaderInfo if it knows about the header. - // FIXME: Subframeworks aren't handled here. Do we care? - bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename); - const DirectoryLookup *CurDir; - const FileEntry *FileEnt = PP.getSourceManager().getFileEntryForID(FileId); - SmallVector, 1> - Includers; - Includers.push_back(std::make_pair(FileEnt, FileEnt->getDir())); - // FIXME: Why don't we call PP.LookupFile here? - const FileEntry *File = PP.getHeaderSearchInfo().LookupFile( - Filename, SourceLocation(), isAngled, Lookup, CurDir, Includers, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr); - - FileExists = File != nullptr; - return true; -} - /// Use a raw lexer to analyze \p FileId, incrementally copying parts of it /// and including content of included files recursively. void InclusionRewriter::Process(FileID FileId, @@ -519,53 +473,33 @@ case tok::pp_elif: { bool elif = (RawToken.getIdentifierInfo()->getPPKeywordID() == tok::pp_elif); - // Rewrite special builtin macros to avoid pulling in host details. + bool isTrue = IsIfAtLocationTrue(RawToken.getLocation()); + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(HashToken.getLocation()), + LocalEOL, Line, /*EnsureNewline=*/true); do { - // Walk over the directive. RawLex.LexFromRawLexer(RawToken); - if (RawToken.is(tok::raw_identifier)) - PP.LookUpIdentifierInfo(RawToken); - - if (RawToken.is(tok::identifier)) { - bool HasFile; - SourceLocation Loc = RawToken.getLocation(); - - // Rewrite __has_include(x) - if (RawToken.getIdentifierInfo()->isStr("__has_include")) { - if (!HandleHasInclude(FileId, RawLex, nullptr, RawToken, - HasFile)) - continue; - // Rewrite __has_include_next(x) - } else if (RawToken.getIdentifierInfo()->isStr( - "__has_include_next")) { - if (DirLookup) - ++DirLookup; - - if (!HandleHasInclude(FileId, RawLex, DirLookup, RawToken, - HasFile)) - continue; - } else { - continue; - } - // Replace the macro with (0) or (1), followed by the commented - // out macro for reference. - OutputContentUpTo(FromFile, NextToWrite, SM.getFileOffset(Loc), - LocalEOL, Line, false); - OS << '(' << (int) HasFile << ")/*"; - OutputContentUpTo(FromFile, NextToWrite, - SM.getFileOffset(RawToken.getLocation()) + - RawToken.getLength(), - LocalEOL, Line, false); - OS << "*/"; - } - } while (RawToken.isNot(tok::eod)); + } while (!RawToken.is(tok::eod) && RawToken.isNot(tok::eof)); + // We need to disable the old condition, but that is tricky. + // Trying to comment it out can easily lead to comment nesting. + // So instead make the condition harmless by making it enclose + // and empty block. Moreover, put it itself inside an #if 0 block + // to disable it from getting evaluated (e.g. __has_include_next + // warns if used from the primary source file). + OS << "#if 0 /* disabled by -frewrite-includes */" << MainEOL; if (elif) { - OutputContentUpTo(FromFile, NextToWrite, - SM.getFileOffset(RawToken.getLocation()) + - RawToken.getLength(), - LocalEOL, Line, /*EnsureNewline=*/ true); - WriteLineInfo(FileName, Line, FileType); + OS << "#if 0" << MainEOL; } + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, /*EnsureNewline=*/true); + // Close the empty block and the disabling block. + OS << "#endif" << MainEOL; + OS << "#endif /* disabled by -frewrite-includes */" << MainEOL; + OS << (elif ? "#elif " : "#if ") << (isTrue ? "1" : "0") + << " /* evaluated by -frewrite-includes */" << MainEOL; + WriteLineInfo(FileName, Line, FileType); break; } case tok::pp_endif: Index: clang/test/Frontend/rewrite-includes-conditions.c =================================================================== --- /dev/null +++ clang/test/Frontend/rewrite-includes-conditions.c @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -verify -E -frewrite-includes -I %S/Inputs %s -o - | FileCheck -strict-whitespace %s +// expected-no-diagnostics + +#define value1 1 +#if value1 +line1 +#else +line2 +#endif + +#define value2 2 + +#if value1 == value2 +line3 +#elif value1 > value2 +line4 +#elif value2 < value2 +line5 +#else +line6 +#endif + +#if __has_include() +#endif + +#define HAS_INCLUDE(x) __has_include(x) + +#if HAS_INCLUDE() +#endif + +/* +#if value1 +commented out +*/ + +#if value1 < value2 \ +|| value1 != value2 +line7 +#endif + +#if value1 /* +*/ +#endif + +// ENDCOMPARE + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if value1 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 6 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if value1 == value2 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 0 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 14 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 0 +// CHECK-NEXT: #elif value1 > value2 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #elif 0 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 16 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 0 +// CHECK-NEXT: #elif value2 < value2 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #elif 0 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 18 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if __has_include() +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 24 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if HAS_INCLUDE() +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 29 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if value1 < value2 \ +// CHECK-NEXT: || value1 != value2 +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 38 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: #if 0 /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if value1 /* +// CHECK-NEXT: */ +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif /* disabled by -frewrite-includes */ +// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */ +// CHECK-NEXT: # 43 "{{.*}}rewrite-includes-conditions.c" + +// CHECK: {{^}}// ENDCOMPARE{{$}} Index: clang/test/Frontend/rewrite-includes.c =================================================================== --- clang/test/Frontend/rewrite-includes.c +++ clang/test/Frontend/rewrite-includes.c @@ -110,12 +110,27 @@ // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 22 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h" 1{{$}} -// CHECK-NEXT: {{^}}#if (0)/*__has_include_next()*/{{$}} -// CHECK-NEXT: {{^}}#elif (0)/*__has_include()*/{{$}} +// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if __has_include_next(){{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if 0 /* evaluated by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} +// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if 0{{$}} +// CHECK-NEXT: {{^}}#elif __has_include(){{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#elif 0 /* evaluated by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 3 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} // CHECK-NEXT: {{^}}#endif{{$}} // CHECK-NEXT: {{^}}# 4 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} -// CHECK-NEXT: {{^}}#if !(1)/*__has_include("rewrite-includes8.h")*/{{$}} +// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if !__has_include("rewrite-includes8.h"){{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if 0 /* evaluated by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}# 5 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} // CHECK-NEXT: {{^}}#endif{{$}} // CHECK-NEXT: {{^}}# 6 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes8.h"{{$}} // CHECK-NEXT: {{^}}# 23 "{{.*}}rewrite-includes.c" 2{{$}} @@ -124,7 +139,12 @@ // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 23 "{{.*}}rewrite-includes.c"{{$}} // CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes9.h" 1{{$}} -// CHECK-NEXT: {{^}}#if (1)/*__has_include_next()*/{{$}} +// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if __has_include_next(){{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#if 1 /* evaluated by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs(/|\\\\)}}rewrite-includes9.h"{{$}} // CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}#include_next {{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} @@ -193,15 +213,32 @@ // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes8.h"{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}#if (0)/*__has_include_next()*/{{$}} -// CHECKNL-NEXT: {{^}}#elif (0)/*__has_include()*/{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if __has_include_next(){{$}} // CHECKNL-NEXT: {{^}}#endif{{$}} -// CHECKNL-NEXT: {{^}}#if !(1)/*__has_include("rewrite-includes8.h")*/{{$}} +// CHECKNL-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* evaluated by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0{{$}} +// CHECKNL-NEXT: {{^}}#elif __has_include(){{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#elif 0 /* evaluated by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if !__has_include("rewrite-includes8.h"){{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* evaluated by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#endif{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes9.h"{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} -// CHECKNL-NEXT: {{^}}#if (1)/*__has_include_next()*/{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if __has_include_next(){{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#endif /* disabled by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 1 /* evaluated by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include_next {{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} Index: clang/test/Modules/preprocess-module.cpp =================================================================== --- clang/test/Modules/preprocess-module.cpp +++ clang/test/Modules/preprocess-module.cpp @@ -51,7 +51,7 @@ // RUN: %clang_cc1 -fmodules -fmodule-file=%t/file.rewrite.pcm %s -I%t -verify -fno-modules-error-recovery -DFILE_REWRITE -DINCLUDE -I%S/Inputs/preprocess // // Check that we can preprocess this user of the .pcm file. -// RUN: %clang_cc1 -fmodules -fmodule-file=%t/file.pcm %s -I%t -E -frewrite-imports -o %t/preprocess-module.ii +// RUN: %clang_cc1 -fmodules -fmodule-file=%t/file.pcm %s -I%t -E -frewrite-imports -DFILE_REWRITE_FULL -o %t/preprocess-module.ii // RUN: %clang_cc1 -fmodules %t/preprocess-module.ii -verify -fno-modules-error-recovery -DFILE_REWRITE_FULL // // Check that language / header search options are ignored when preprocessing from a .pcm file.