make -frewrite-includes also rewrite conditions in #if/#elif

Those conditions may use __has_include, which needs to be rewritten.
The existing code has already tried to rewrite just __has_include,
but it didn't work with macro expansion, so e.g. Qt's
"#define QT_HAS_INCLUDE(x) __has_include(x)" didn't get handled
properly. Since the preprocessor run knows what each condition evaluates
to, just rewrite the entire condition. This of course requires that
the -frewrite-include pass has the same setup as the following
compilation, but that has always been the requirement.

Differential Revision: https://reviews.llvm.org/D63508

llvm-svn: 372248
This commit is contained in:
Lubos Lunak 2019-09-18 19:09:41 +00:00
parent 5741d19f04
commit 533434fc72
4 changed files with 207 additions and 129 deletions

View File

@ -49,6 +49,8 @@ class InclusionRewriter : public PPCallbacks {
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;
@ -78,6 +80,10 @@ private:
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 @@ private:
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 @@ void InclusionRewriter::InclusionDirective(SourceLocation HashLoc,
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 @@ InclusionRewriter::FindEnteredModule(SourceLocation Loc) const {
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 @@ StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex,
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,
@ -519,53 +473,33 @@ 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:

View File

@ -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 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{{$}}

View File

@ -110,12 +110,27 @@ A(1,2)
// 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{{$}}
@ -124,7 +139,12 @@ A(1,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 */{{$}}
@ -193,15 +213,32 @@ A(1,2)
// 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 */{{$}}

View File

@ -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.