std::map<unsigned, const Module *> ModuleIncludes;
/// Tracks where inclusions that enter modules (in a module build) are found.
std::map<unsigned, const Module *> ModuleEntryIncludes;
+ /// Tracks where #if and #elif directives get evaluated and whether to true.
+ std::map<unsigned, bool> IfConditions;
/// Used transitively for building up the FileIncludes mapping over the
/// various \c PPCallbacks callbacks.
SourceLocation LastInclusionLocation;
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());
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);
};
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 *
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) {
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<char, 128> 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<std::pair<const FileEntry *, const DirectoryEntry *>, 1>
- Includers;
- Includers.push_back(std::make_pair(FileEnt, FileEnt->getDir()));
- // FIXME: Why don't we call PP.LookupFile here?
- Optional<FileEntryRef> File = PP.getHeaderSearchInfo().LookupFile(
- Filename, SourceLocation(), isAngled, Lookup, CurDir, Includers, nullptr,
- nullptr, nullptr, nullptr, nullptr, nullptr);
-
- FileExists = File.hasValue();
- 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,
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:
--- /dev/null
+// 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 value1 < value2
+line5
+#else
+line6
+#endif
+
+#if __has_include(<rewrite-includes1.h>)
+#endif
+
+#define HAS_INCLUDE(x) __has_include(x)
+
+#if HAS_INCLUDE(<rewrite-includes1.h>)
+#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 value1 < value2
+// CHECK-NEXT: #endif
+// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
+// CHECK-NEXT: #elif 1 /* evaluated by -frewrite-includes */
+// CHECK-NEXT: # 18 "{{.*}}rewrite-includes-conditions.c"
+
+// CHECK: #if 0 /* disabled by -frewrite-includes */
+// CHECK-NEXT: #if __has_include(<rewrite-includes1.h>)
+// 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(<rewrite-includes1.h>)
+// 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{{$}}
// 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(<rewrite-includes8.h>)*/{{$}}
-// CHECK-NEXT: {{^}}#elif (0)/*__has_include(<rewrite-includes8.hfail>)*/{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#if __has_include_next(<rewrite-includes8.h>){{$}}
+// 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(<rewrite-includes8.hfail>){{$}}
+// 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{{$}}
// 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(<rewrite-includes9.h>)*/{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#if __has_include_next(<rewrite-includes9.h>){{$}}
+// 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 <rewrite-includes9.h>{{$}}
// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
// 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(<rewrite-includes8.h>)*/{{$}}
-// CHECKNL-NEXT: {{^}}#elif (0)/*__has_include(<rewrite-includes8.hfail>)*/{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#if __has_include_next(<rewrite-includes8.h>){{$}}
// 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(<rewrite-includes8.hfail>){{$}}
+// 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(<rewrite-includes9.h>)*/{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* disabled by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#if __has_include_next(<rewrite-includes9.h>){{$}}
+// 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 <rewrite-includes9.h>{{$}}
// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}