[preamble] Also record the "skipping" state of the preprocessor

When a preamble ends in a conditional preprocessor block that is being
skipped, the preprocessor needs to continue skipping that block when
the preamble is used.

This fixes PR34570.

llvm-svn: 317308
This commit is contained in:
Erik Verbruggen 2017-11-03 09:40:07 +00:00
parent 8c825db25e
commit 4d1eb2d5cb
7 changed files with 102 additions and 16 deletions

View File

@ -286,6 +286,23 @@ class Preprocessor {
/// This is used when loading a precompiled preamble.
std::pair<int, bool> SkipMainFilePreamble;
public:
struct PreambleSkipInfo {
PreambleSkipInfo(SourceLocation HashTokenLoc, SourceLocation IfTokenLoc,
bool FoundNonSkipPortion, bool FoundElse,
SourceLocation ElseLoc)
: HashTokenLoc(HashTokenLoc), IfTokenLoc(IfTokenLoc),
FoundNonSkipPortion(FoundNonSkipPortion), FoundElse(FoundElse),
ElseLoc(ElseLoc) {}
SourceLocation HashTokenLoc;
SourceLocation IfTokenLoc;
bool FoundNonSkipPortion;
bool FoundElse;
SourceLocation ElseLoc;
};
private:
class PreambleConditionalStackStore {
enum State {
Off = 0,
@ -319,6 +336,12 @@ class Preprocessor {
bool hasRecordedPreamble() const { return !ConditionalStack.empty(); }
bool reachedEOFWhileSkipping() const { return SkipInfo.hasValue(); }
void clearSkipInfo() { SkipInfo.reset(); }
llvm::Optional<PreambleSkipInfo> SkipInfo;
private:
SmallVector<PPConditionalInfo, 4> ConditionalStack;
State ConditionalStackState;
@ -1839,7 +1862,7 @@ private:
/// \p FoundElse is false, then \#else directives are ok, if not, then we have
/// already seen one so a \#else directive is a duplicate. When this returns,
/// the caller can lex the first valid token.
void SkipExcludedConditionalBlock(const Token &HashToken,
void SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
SourceLocation IfTokenLoc,
bool FoundNonSkipPortion, bool FoundElse,
SourceLocation ElseLoc = SourceLocation());
@ -2019,9 +2042,15 @@ public:
PreambleConditionalStack.setStack(s);
}
void setReplayablePreambleConditionalStack(ArrayRef<PPConditionalInfo> s) {
void setReplayablePreambleConditionalStack(ArrayRef<PPConditionalInfo> s,
llvm::Optional<PreambleSkipInfo> SkipInfo) {
PreambleConditionalStack.startReplaying();
PreambleConditionalStack.setStack(s);
PreambleConditionalStack.SkipInfo = SkipInfo;
}
llvm::Optional<PreambleSkipInfo> getPreambleSkipInfo() const {
return PreambleConditionalStack.SkipInfo;
}
private:

View File

@ -350,7 +350,7 @@ void Preprocessor::CheckEndOfDirective(const char *DirType, bool EnableMacros) {
/// If ElseOk is true, then \#else directives are ok, if not, then we have
/// already seen one so a \#else directive is a duplicate. When this returns,
/// the caller can lex the first valid token.
void Preprocessor::SkipExcludedConditionalBlock(const Token &HashToken,
void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
SourceLocation IfTokenLoc,
bool FoundNonSkipPortion,
bool FoundElse,
@ -358,8 +358,11 @@ void Preprocessor::SkipExcludedConditionalBlock(const Token &HashToken,
++NumSkipped;
assert(!CurTokenLexer && CurPPLexer && "Lexing a macro, not a file?");
CurPPLexer->pushConditionalLevel(IfTokenLoc, /*isSkipping*/false,
FoundNonSkipPortion, FoundElse);
if (PreambleConditionalStack.reachedEOFWhileSkipping())
PreambleConditionalStack.clearSkipInfo();
else
CurPPLexer->pushConditionalLevel(IfTokenLoc, /*isSkipping*/ false,
FoundNonSkipPortion, FoundElse);
if (CurPTHLexer) {
PTHSkipExcludedConditionalBlock();
@ -385,6 +388,9 @@ void Preprocessor::SkipExcludedConditionalBlock(const Token &HashToken,
// We don't emit errors for unterminated conditionals here,
// Lexer::LexEndOfFile can do that propertly.
// Just return and let the caller lex after this #include.
if (PreambleConditionalStack.isRecording())
PreambleConditionalStack.SkipInfo.emplace(
HashTokenLoc, IfTokenLoc, FoundNonSkipPortion, FoundElse, ElseLoc);
break;
}
@ -554,7 +560,7 @@ void Preprocessor::SkipExcludedConditionalBlock(const Token &HashToken,
if (Callbacks)
Callbacks->SourceRangeSkipped(
SourceRange(HashToken.getLocation(), CurPPLexer->getSourceLocation()),
SourceRange(HashTokenLoc, CurPPLexer->getSourceLocation()),
Tok.getLocation());
}
@ -2676,7 +2682,8 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
if (MacroNameTok.is(tok::eod)) {
// Skip code until we get to #endif. This helps with recovery by not
// emitting an error when the #endif is reached.
SkipExcludedConditionalBlock(HashToken, DirectiveTok.getLocation(),
SkipExcludedConditionalBlock(HashToken.getLocation(),
DirectiveTok.getLocation(),
/*Foundnonskip*/ false, /*FoundElse*/ false);
return;
}
@ -2725,7 +2732,8 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
/*foundelse*/false);
} else {
// No, skip the contents of this block.
SkipExcludedConditionalBlock(HashToken, DirectiveTok.getLocation(),
SkipExcludedConditionalBlock(HashToken.getLocation(),
DirectiveTok.getLocation(),
/*Foundnonskip*/ false,
/*FoundElse*/ false);
}
@ -2772,7 +2780,7 @@ void Preprocessor::HandleIfDirective(Token &IfToken,
/*foundnonskip*/true, /*foundelse*/false);
} else {
// No, skip the contents of this block.
SkipExcludedConditionalBlock(HashToken, IfToken.getLocation(),
SkipExcludedConditionalBlock(HashToken.getLocation(), IfToken.getLocation(),
/*Foundnonskip*/ false,
/*FoundElse*/ false);
}
@ -2837,7 +2845,8 @@ void Preprocessor::HandleElseDirective(Token &Result, const Token &HashToken) {
}
// Finally, skip the rest of the contents of this block.
SkipExcludedConditionalBlock(HashToken, CI.IfLoc, /*Foundnonskip*/ true,
SkipExcludedConditionalBlock(HashToken.getLocation(), CI.IfLoc,
/*Foundnonskip*/ true,
/*FoundElse*/ true, Result.getLocation());
}
@ -2881,7 +2890,7 @@ void Preprocessor::HandleElifDirective(Token &ElifToken,
}
// Finally, skip the rest of the contents of this block.
SkipExcludedConditionalBlock(HashToken, CI.IfLoc, /*Foundnonskip*/ true,
/*FoundElse*/ CI.FoundElse,
ElifToken.getLocation());
SkipExcludedConditionalBlock(
HashToken.getLocation(), CI.IfLoc, /*Foundnonskip*/ true,
/*FoundElse*/ CI.FoundElse, ElifToken.getLocation());
}

View File

@ -550,6 +550,13 @@ void Preprocessor::replayPreambleConditionalStack() {
"CurPPLexer is null when calling replayPreambleConditionalStack.");
CurPPLexer->setConditionalLevels(PreambleConditionalStack.getStack());
PreambleConditionalStack.doneReplaying();
if (PreambleConditionalStack.reachedEOFWhileSkipping())
SkipExcludedConditionalBlock(
PreambleConditionalStack.SkipInfo->HashTokenLoc,
PreambleConditionalStack.SkipInfo->IfTokenLoc,
PreambleConditionalStack.SkipInfo->FoundNonSkipPortion,
PreambleConditionalStack.SkipInfo->FoundElse,
PreambleConditionalStack.SkipInfo->ElseLoc);
}
}

View File

@ -2995,8 +2995,20 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
case PP_CONDITIONAL_STACK:
if (!Record.empty()) {
unsigned Idx = 0, End = Record.size() - 1;
bool ReachedEOFWhileSkipping = Record[Idx++];
llvm::Optional<Preprocessor::PreambleSkipInfo> SkipInfo;
if (ReachedEOFWhileSkipping) {
SourceLocation HashToken = ReadSourceLocation(F, Record, Idx);
SourceLocation IfTokenLoc = ReadSourceLocation(F, Record, Idx);
bool FoundNonSkipPortion = Record[Idx++];
bool FoundElse = Record[Idx++];
SourceLocation ElseLoc = ReadSourceLocation(F, Record, Idx);
SkipInfo.emplace(HashToken, IfTokenLoc, FoundNonSkipPortion,
FoundElse, ElseLoc);
}
SmallVector<PPConditionalInfo, 4> ConditionalStack;
for (unsigned Idx = 0, N = Record.size() - 1; Idx < N; /* in loop */) {
while (Idx < End) {
auto Loc = ReadSourceLocation(F, Record, Idx);
bool WasSkipping = Record[Idx++];
bool FoundNonSkip = Record[Idx++];
@ -3004,7 +3016,7 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
ConditionalStack.push_back(
{Loc, WasSkipping, FoundNonSkip, FoundElse});
}
PP.setReplayablePreambleConditionalStack(ConditionalStack);
PP.setReplayablePreambleConditionalStack(ConditionalStack, SkipInfo);
}
break;

View File

@ -2407,6 +2407,17 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
if (PP.isRecordingPreamble() && PP.hasRecordedPreamble()) {
assert(!IsModule);
auto SkipInfo = PP.getPreambleSkipInfo();
if (SkipInfo.hasValue()) {
Record.push_back(true);
AddSourceLocation(SkipInfo->HashTokenLoc, Record);
AddSourceLocation(SkipInfo->IfTokenLoc, Record);
Record.push_back(SkipInfo->FoundNonSkipPortion);
Record.push_back(SkipInfo->FoundElse);
AddSourceLocation(SkipInfo->ElseLoc, Record);
} else {
Record.push_back(false);
}
for (const auto &Cond : PP.getPreambleConditionalStack()) {
AddSourceLocation(Cond.IfLoc, Record);
Record.push_back(Cond.WasSkipping);

View File

@ -3,6 +3,8 @@
// RUN: | FileCheck %s --implicit-check-not "error:"
#ifdef FOO_H
void foo();
void foo() {}
#endif
int foo() { return 0; }

View File

@ -0,0 +1,16 @@
// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 5 \
// RUN: local -std=c++14 %s 2>&1 \
// RUN: | FileCheck %s --implicit-check-not "error:"
#ifdef MYCPLUSPLUS
extern "C" {
#endif
#ifdef MYCPLUSPLUS
}
#endif
int main()
{
return 0;
}