Skip to content

Commit 533434f

Browse files
committed
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
1 parent 5741d19 commit 533434f

File tree

4 files changed

+207
-129
lines changed

4 files changed

+207
-129
lines changed

clang/lib/Frontend/Rewrite/InclusionRewriter.cpp

Lines changed: 54 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class InclusionRewriter : public PPCallbacks {
4949
std::map<unsigned, const Module *> ModuleIncludes;
5050
/// Tracks where inclusions that enter modules (in a module build) are found.
5151
std::map<unsigned, const Module *> ModuleEntryIncludes;
52+
/// Tracks where #if and #elif directives get evaluated and whether to true.
53+
std::map<unsigned, bool> IfConditions;
5254
/// Used transitively for building up the FileIncludes mapping over the
5355
/// various \c PPCallbacks callbacks.
5456
SourceLocation LastInclusionLocation;
@@ -78,6 +80,10 @@ class InclusionRewriter : public PPCallbacks {
7880
StringRef SearchPath, StringRef RelativePath,
7981
const Module *Imported,
8082
SrcMgr::CharacteristicKind FileType) override;
83+
void If(SourceLocation Loc, SourceRange ConditionRange,
84+
ConditionValueKind ConditionValue) override;
85+
void Elif(SourceLocation Loc, SourceRange ConditionRange,
86+
ConditionValueKind ConditionValue, SourceLocation IfLoc) override;
8187
void WriteLineInfo(StringRef Filename, int Line,
8288
SrcMgr::CharacteristicKind FileType,
8389
StringRef Extra = StringRef());
@@ -89,12 +95,10 @@ class InclusionRewriter : public PPCallbacks {
8995
void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken,
9096
const MemoryBuffer &FromFile, StringRef EOL,
9197
unsigned &NextToWrite, int &Lines);
92-
bool HandleHasInclude(FileID FileId, Lexer &RawLex,
93-
const DirectoryLookup *Lookup, Token &Tok,
94-
bool &FileExists);
9598
const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) const;
9699
const Module *FindModuleAtLocation(SourceLocation Loc) const;
97100
const Module *FindEnteredModule(SourceLocation Loc) const;
101+
bool IsIfAtLocationTrue(SourceLocation Loc) const;
98102
StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken);
99103
};
100104

@@ -203,6 +207,23 @@ void InclusionRewriter::InclusionDirective(SourceLocation HashLoc,
203207
LastInclusionLocation = HashLoc;
204208
}
205209

210+
void InclusionRewriter::If(SourceLocation Loc, SourceRange ConditionRange,
211+
ConditionValueKind ConditionValue) {
212+
auto P = IfConditions.insert(
213+
std::make_pair(Loc.getRawEncoding(), ConditionValue == CVK_True));
214+
(void)P;
215+
assert(P.second && "Unexpected revisitation of the same if directive");
216+
}
217+
218+
void InclusionRewriter::Elif(SourceLocation Loc, SourceRange ConditionRange,
219+
ConditionValueKind ConditionValue,
220+
SourceLocation IfLoc) {
221+
auto P = IfConditions.insert(
222+
std::make_pair(Loc.getRawEncoding(), ConditionValue == CVK_True));
223+
(void)P;
224+
assert(P.second && "Unexpected revisitation of the same elif directive");
225+
}
226+
206227
/// Simple lookup for a SourceLocation (specifically one denoting the hash in
207228
/// an inclusion directive) in the map of inclusion information, FileChanges.
208229
const InclusionRewriter::IncludedFile *
@@ -233,6 +254,13 @@ InclusionRewriter::FindEnteredModule(SourceLocation Loc) const {
233254
return nullptr;
234255
}
235256

257+
bool InclusionRewriter::IsIfAtLocationTrue(SourceLocation Loc) const {
258+
const auto I = IfConditions.find(Loc.getRawEncoding());
259+
if (I != IfConditions.end())
260+
return I->second;
261+
return false;
262+
}
263+
236264
/// Detect the likely line ending style of \p FromFile by examining the first
237265
/// newline found within it.
238266
static StringRef DetectEOL(const MemoryBuffer &FromFile) {
@@ -346,80 +374,6 @@ StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex,
346374
return StringRef();
347375
}
348376

349-
// Expand __has_include and __has_include_next if possible. If there's no
350-
// definitive answer return false.
351-
bool InclusionRewriter::HandleHasInclude(
352-
FileID FileId, Lexer &RawLex, const DirectoryLookup *Lookup, Token &Tok,
353-
bool &FileExists) {
354-
// Lex the opening paren.
355-
RawLex.LexFromRawLexer(Tok);
356-
if (Tok.isNot(tok::l_paren))
357-
return false;
358-
359-
RawLex.LexFromRawLexer(Tok);
360-
361-
SmallString<128> FilenameBuffer;
362-
StringRef Filename;
363-
// Since the raw lexer doesn't give us angle_literals we have to parse them
364-
// ourselves.
365-
// FIXME: What to do if the file name is a macro?
366-
if (Tok.is(tok::less)) {
367-
RawLex.LexFromRawLexer(Tok);
368-
369-
FilenameBuffer += '<';
370-
do {
371-
if (Tok.is(tok::eod)) // Sanity check.
372-
return false;
373-
374-
if (Tok.is(tok::raw_identifier))
375-
PP.LookUpIdentifierInfo(Tok);
376-
377-
// Get the string piece.
378-
SmallVector<char, 128> TmpBuffer;
379-
bool Invalid = false;
380-
StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid);
381-
if (Invalid)
382-
return false;
383-
384-
FilenameBuffer += TmpName;
385-
386-
RawLex.LexFromRawLexer(Tok);
387-
} while (Tok.isNot(tok::greater));
388-
389-
FilenameBuffer += '>';
390-
Filename = FilenameBuffer;
391-
} else {
392-
if (Tok.isNot(tok::string_literal))
393-
return false;
394-
395-
bool Invalid = false;
396-
Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid);
397-
if (Invalid)
398-
return false;
399-
}
400-
401-
// Lex the closing paren.
402-
RawLex.LexFromRawLexer(Tok);
403-
if (Tok.isNot(tok::r_paren))
404-
return false;
405-
406-
// Now ask HeaderInfo if it knows about the header.
407-
// FIXME: Subframeworks aren't handled here. Do we care?
408-
bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename);
409-
const DirectoryLookup *CurDir;
410-
const FileEntry *FileEnt = PP.getSourceManager().getFileEntryForID(FileId);
411-
SmallVector<std::pair<const FileEntry *, const DirectoryEntry *>, 1>
412-
Includers;
413-
Includers.push_back(std::make_pair(FileEnt, FileEnt->getDir()));
414-
// FIXME: Why don't we call PP.LookupFile here?
415-
Optional<FileEntryRef> File = PP.getHeaderSearchInfo().LookupFile(
416-
Filename, SourceLocation(), isAngled, Lookup, CurDir, Includers, nullptr,
417-
nullptr, nullptr, nullptr, nullptr, nullptr);
418-
419-
FileExists = File.hasValue();
420-
return true;
421-
}
422-
423377
/// Use a raw lexer to analyze \p FileId, incrementally copying parts of it
424378
/// and including content of included files recursively.
425379
void InclusionRewriter::Process(FileID FileId,
@@ -519,53 +473,33 @@ void InclusionRewriter::Process(FileID FileId,
519473
case tok::pp_elif: {
520474
bool elif = (RawToken.getIdentifierInfo()->getPPKeywordID() ==
521475
tok::pp_elif);
522-
// Rewrite special builtin macros to avoid pulling in host details.
476+
bool isTrue = IsIfAtLocationTrue(RawToken.getLocation());
477+
OutputContentUpTo(FromFile, NextToWrite,
478+
SM.getFileOffset(HashToken.getLocation()),
479+
LocalEOL, Line, /*EnsureNewline=*/true);
523480
do {
524-
// Walk over the directive.
525481
RawLex.LexFromRawLexer(RawToken);
526-
if (RawToken.is(tok::raw_identifier))
527-
PP.LookUpIdentifierInfo(RawToken);
528-
529-
if (RawToken.is(tok::identifier)) {
530-
bool HasFile;
531-
SourceLocation Loc = RawToken.getLocation();
532-
533-
// Rewrite __has_include(x)
534-
if (RawToken.getIdentifierInfo()->isStr("__has_include")) {
535-
if (!HandleHasInclude(FileId, RawLex, nullptr, RawToken,
536-
HasFile))
537-
continue;
538-
// Rewrite __has_include_next(x)
539-
} else if (RawToken.getIdentifierInfo()->isStr(
540-
"__has_include_next")) {
541-
if (DirLookup)
542-
++DirLookup;
543-
544-
if (!HandleHasInclude(FileId, RawLex, DirLookup, RawToken,
545-
HasFile))
546-
continue;
547-
} else {
548-
continue;
549-
}
550-
// Replace the macro with (0) or (1), followed by the commented
551-
// out macro for reference.
552-
OutputContentUpTo(FromFile, NextToWrite, SM.getFileOffset(Loc),
553-
LocalEOL, Line, false);
554-
OS << '(' << (int) HasFile << ")/*";
555-
OutputContentUpTo(FromFile, NextToWrite,
556-
SM.getFileOffset(RawToken.getLocation()) +
557-
RawToken.getLength(),
558-
LocalEOL, Line, false);
559-
OS << "*/";
560-
}
561-
} while (RawToken.isNot(tok::eod));
482+
} while (!RawToken.is(tok::eod) && RawToken.isNot(tok::eof));
483+
// We need to disable the old condition, but that is tricky.
484+
// Trying to comment it out can easily lead to comment nesting.
485+
// So instead make the condition harmless by making it enclose
486+
// and empty block. Moreover, put it itself inside an #if 0 block
487+
// to disable it from getting evaluated (e.g. __has_include_next
488+
// warns if used from the primary source file).
489+
OS << "#if 0 /* disabled by -frewrite-includes */" << MainEOL;
562490
if (elif) {
563-
OutputContentUpTo(FromFile, NextToWrite,
564-
SM.getFileOffset(RawToken.getLocation()) +
565-
RawToken.getLength(),
566-
LocalEOL, Line, /*EnsureNewline=*/ true);
567-
WriteLineInfo(FileName, Line, FileType);
491+
OS << "#if 0" << MainEOL;
568492
}
493+
OutputContentUpTo(FromFile, NextToWrite,
494+
SM.getFileOffset(RawToken.getLocation()) +
495+
RawToken.getLength(),
496+
LocalEOL, Line, /*EnsureNewline=*/true);
497+
// Close the empty block and the disabling block.
498+
OS << "#endif" << MainEOL;
499+
OS << "#endif /* disabled by -frewrite-includes */" << MainEOL;
500+
OS << (elif ? "#elif " : "#if ") << (isTrue ? "1" : "0")
501+
<< " /* evaluated by -frewrite-includes */" << MainEOL;
502+
WriteLineInfo(FileName, Line, FileType);
569503
break;
570504
}
571505
case tok::pp_endif:
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// RUN: %clang_cc1 -verify -E -frewrite-includes -I %S/Inputs %s -o - | FileCheck -strict-whitespace %s
2+
// expected-no-diagnostics
3+
4+
#define value1 1
5+
#if value1
6+
line1
7+
#else
8+
line2
9+
#endif
10+
11+
#define value2 2
12+
13+
#if value1 == value2
14+
line3
15+
#elif value1 > value2
16+
line4
17+
#elif value1 < value2
18+
line5
19+
#else
20+
line6
21+
#endif
22+
23+
#if __has_include(<rewrite-includes1.h>)
24+
#endif
25+
26+
#define HAS_INCLUDE(x) __has_include(x)
27+
28+
#if HAS_INCLUDE(<rewrite-includes1.h>)
29+
#endif
30+
31+
/*
32+
#if value1
33+
commented out
34+
*/
35+
36+
#if value1 < value2 \
37+
|| value1 != value2
38+
line7
39+
#endif
40+
41+
#if value1 /*
42+
*/
43+
#endif
44+
45+
// ENDCOMPARE
46+
47+
// CHECK: #if 0 /* disabled by -frewrite-includes */
48+
// CHECK-NEXT: #if value1
49+
// CHECK-NEXT: #endif
50+
// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
51+
// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */
52+
// CHECK-NEXT: # 6 "{{.*}}rewrite-includes-conditions.c"
53+
54+
// CHECK: #if 0 /* disabled by -frewrite-includes */
55+
// CHECK-NEXT: #if value1 == value2
56+
// CHECK-NEXT: #endif
57+
// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
58+
// CHECK-NEXT: #if 0 /* evaluated by -frewrite-includes */
59+
// CHECK-NEXT: # 14 "{{.*}}rewrite-includes-conditions.c"
60+
61+
// CHECK: #if 0 /* disabled by -frewrite-includes */
62+
// CHECK-NEXT: #if 0
63+
// CHECK-NEXT: #elif value1 > value2
64+
// CHECK-NEXT: #endif
65+
// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
66+
// CHECK-NEXT: #elif 0 /* evaluated by -frewrite-includes */
67+
// CHECK-NEXT: # 16 "{{.*}}rewrite-includes-conditions.c"
68+
69+
// CHECK: #if 0 /* disabled by -frewrite-includes */
70+
// CHECK-NEXT: #if 0
71+
// CHECK-NEXT: #elif value1 < value2
72+
// CHECK-NEXT: #endif
73+
// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
74+
// CHECK-NEXT: #elif 1 /* evaluated by -frewrite-includes */
75+
// CHECK-NEXT: # 18 "{{.*}}rewrite-includes-conditions.c"
76+
77+
// CHECK: #if 0 /* disabled by -frewrite-includes */
78+
// CHECK-NEXT: #if __has_include(<rewrite-includes1.h>)
79+
// CHECK-NEXT: #endif
80+
// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
81+
// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */
82+
// CHECK-NEXT: # 24 "{{.*}}rewrite-includes-conditions.c"
83+
84+
// CHECK: #if 0 /* disabled by -frewrite-includes */
85+
// CHECK-NEXT: #if HAS_INCLUDE(<rewrite-includes1.h>)
86+
// CHECK-NEXT: #endif
87+
// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
88+
// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */
89+
// CHECK-NEXT: # 29 "{{.*}}rewrite-includes-conditions.c"
90+
91+
// CHECK: #if 0 /* disabled by -frewrite-includes */
92+
// CHECK-NEXT: #if value1 < value2 \
93+
// CHECK-NEXT: || value1 != value2
94+
// CHECK-NEXT: #endif
95+
// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
96+
// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */
97+
// CHECK-NEXT: # 38 "{{.*}}rewrite-includes-conditions.c"
98+
99+
// CHECK: #if 0 /* disabled by -frewrite-includes */
100+
// CHECK-NEXT: #if value1 /*
101+
// CHECK-NEXT: */
102+
// CHECK-NEXT: #endif
103+
// CHECK-NEXT: #endif /* disabled by -frewrite-includes */
104+
// CHECK-NEXT: #if 1 /* evaluated by -frewrite-includes */
105+
// CHECK-NEXT: # 43 "{{.*}}rewrite-includes-conditions.c"
106+
107+
// CHECK: {{^}}// ENDCOMPARE{{$}}

0 commit comments

Comments
 (0)