Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 0289cea

Browse files
committed
Allow for unfinished #if blocks in preambles
Previously, a preamble only included #if blocks (and friends like ifdef) if there was a corresponding #endif before any declaration or definition. The problem is that any header file that uses include guards will not have a preamble generated, which can make code-completion very slow. To prevent errors about unbalanced preprocessor conditionals in the preamble, and unbalanced preprocessor conditionals after a preamble containing unfinished conditionals, the conditional stack is stored in the pch file. This fixes PR26045. Differential Revision: http://reviews.llvm.org/D15994 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@304207 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent bccb6a4 commit 0289cea

File tree

12 files changed

+161
-35
lines changed

12 files changed

+161
-35
lines changed

Diff for: include/clang/Lex/Preprocessor.h

+64
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,44 @@ class Preprocessor {
283283
/// This is used when loading a precompiled preamble.
284284
std::pair<int, bool> SkipMainFilePreamble;
285285

286+
class PreambleConditionalStackStore {
287+
enum State {
288+
Off = 0,
289+
Recording = 1,
290+
Replaying = 2,
291+
};
292+
293+
public:
294+
PreambleConditionalStackStore() : ConditionalStackState(Off) {}
295+
296+
void startRecording() { ConditionalStackState = Recording; }
297+
void startReplaying() { ConditionalStackState = Replaying; }
298+
bool isRecording() const { return ConditionalStackState == Recording; }
299+
bool isReplaying() const { return ConditionalStackState == Replaying; }
300+
301+
ArrayRef<PPConditionalInfo> getStack() const {
302+
return ConditionalStack;
303+
}
304+
305+
void doneReplaying() {
306+
ConditionalStack.clear();
307+
ConditionalStackState = Off;
308+
}
309+
310+
void setStack(ArrayRef<PPConditionalInfo> s) {
311+
if (!isRecording() && !isReplaying())
312+
return;
313+
ConditionalStack.clear();
314+
ConditionalStack.append(s.begin(), s.end());
315+
}
316+
317+
bool hasRecordedPreamble() const { return !ConditionalStack.empty(); }
318+
319+
private:
320+
SmallVector<PPConditionalInfo, 4> ConditionalStack;
321+
State ConditionalStackState;
322+
} PreambleConditionalStack;
323+
286324
/// \brief The current top of the stack that we're lexing from if
287325
/// not expanding a macro and we are lexing directly from source code.
288326
///
@@ -1695,6 +1733,11 @@ class Preprocessor {
16951733
/// \brief Return true if we're in the top-level file, not in a \#include.
16961734
bool isInPrimaryFile() const;
16971735

1736+
/// \brief Return true if we're in the main file (specifically, if we are 0
1737+
/// (zero) levels deep \#include. This is used by the lexer to determine if
1738+
/// it needs to generate errors about unterminated \#if directives.
1739+
bool isInMainFile() const;
1740+
16981741
/// \brief Handle cases where the \#include name is expanded
16991742
/// from a macro as multiple tokens, which need to be glued together.
17001743
///
@@ -1932,6 +1975,27 @@ class Preprocessor {
19321975
Module *M,
19331976
SourceLocation MLoc);
19341977

1978+
bool isRecordingPreamble() const {
1979+
return PreambleConditionalStack.isRecording();
1980+
}
1981+
1982+
bool hasRecordedPreamble() const {
1983+
return PreambleConditionalStack.hasRecordedPreamble();
1984+
}
1985+
1986+
ArrayRef<PPConditionalInfo> getPreambleConditionalStack() const {
1987+
return PreambleConditionalStack.getStack();
1988+
}
1989+
1990+
void setRecordedPreambleConditionalStack(ArrayRef<PPConditionalInfo> s) {
1991+
PreambleConditionalStack.setStack(s);
1992+
}
1993+
1994+
void setReplayablePreambleConditionalStack(ArrayRef<PPConditionalInfo> s) {
1995+
PreambleConditionalStack.startReplaying();
1996+
PreambleConditionalStack.setStack(s);
1997+
}
1998+
19351999
private:
19362000
// Macro handling.
19372001
void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterTopLevelIfndef);

Diff for: include/clang/Lex/PreprocessorLexer.h

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "clang/Lex/MultipleIncludeOpt.h"
1919
#include "clang/Lex/Token.h"
20+
#include "llvm/ADT/ArrayRef.h"
2021
#include "llvm/ADT/SmallVector.h"
2122

2223
namespace clang {
@@ -176,6 +177,11 @@ class PreprocessorLexer {
176177
conditional_iterator conditional_end() const {
177178
return ConditionalStack.end();
178179
}
180+
181+
void setConditionalLevels(ArrayRef<PPConditionalInfo> CL) {
182+
ConditionalStack.clear();
183+
ConditionalStack.append(CL.begin(), CL.end());
184+
}
179185
};
180186

181187
} // end namespace clang

Diff for: include/clang/Lex/PreprocessorOptions.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,14 @@ class PreprocessorOptions {
8080
/// The boolean indicates whether the preamble ends at the start of a new
8181
/// line.
8282
std::pair<unsigned, bool> PrecompiledPreambleBytes;
83-
83+
84+
/// \brief True indicates that a preamble is being generated.
85+
///
86+
/// When the lexer is done, one of the things that need to be preserved is the
87+
/// conditional #if stack, so the ASTWriter/ASTReader can save/restore it when
88+
/// processing the rest of the file.
89+
bool GeneratePreamble;
90+
8491
/// The implicit PTH input included at the start of the translation unit, or
8592
/// empty.
8693
std::string ImplicitPTHInclude;
@@ -144,6 +151,7 @@ class PreprocessorOptions {
144151
AllowPCHWithCompilerErrors(false),
145152
DumpDeserializedPCHDecls(false),
146153
PrecompiledPreambleBytes(0, true),
154+
GeneratePreamble(false),
147155
RemappedFilesKeepOriginalName(true),
148156
RetainRemappedFileBuffers(false),
149157
ObjCXXARCStandardLibrary(ARCXX_nolib) { }

Diff for: include/clang/Serialization/ASTBitCodes.h

+3
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,9 @@ namespace clang {
607607

608608
/// \brief Record code for \#pragma pack options.
609609
PACK_PRAGMA_OPTIONS = 61,
610+
611+
/// \brief The stack of open #ifs/#ifdefs recorded in a preamble.
612+
PP_CONDITIONAL_STACK = 62,
610613
};
611614

612615
/// \brief Record types used within a source manager block.

Diff for: lib/Frontend/ASTUnit.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine(
19991999
PreprocessorOptions &PPOpts = CI->getPreprocessorOpts();
20002000
PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName;
20012001
PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors;
2002+
PPOpts.GeneratePreamble = PrecompilePreambleAfterNParses != 0;
20022003

20032004
// Override the resources path.
20042005
CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath;

Diff for: lib/Lex/Lexer.cpp

+11-28
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,6 @@ namespace {
550550

551551
enum PreambleDirectiveKind {
552552
PDK_Skipped,
553-
PDK_StartIf,
554-
PDK_EndIf,
555553
PDK_Unknown
556554
};
557555

@@ -574,8 +572,6 @@ std::pair<unsigned, bool> Lexer::ComputePreamble(StringRef Buffer,
574572

575573
bool InPreprocessorDirective = false;
576574
Token TheTok;
577-
Token IfStartTok;
578-
unsigned IfCount = 0;
579575
SourceLocation ActiveCommentLoc;
580576

581577
unsigned MaxLineOffset = 0;
@@ -658,33 +654,18 @@ std::pair<unsigned, bool> Lexer::ComputePreamble(StringRef Buffer,
658654
.Case("sccs", PDK_Skipped)
659655
.Case("assert", PDK_Skipped)
660656
.Case("unassert", PDK_Skipped)
661-
.Case("if", PDK_StartIf)
662-
.Case("ifdef", PDK_StartIf)
663-
.Case("ifndef", PDK_StartIf)
657+
.Case("if", PDK_Skipped)
658+
.Case("ifdef", PDK_Skipped)
659+
.Case("ifndef", PDK_Skipped)
664660
.Case("elif", PDK_Skipped)
665661
.Case("else", PDK_Skipped)
666-
.Case("endif", PDK_EndIf)
662+
.Case("endif", PDK_Skipped)
667663
.Default(PDK_Unknown);
668664

669665
switch (PDK) {
670666
case PDK_Skipped:
671667
continue;
672668

673-
case PDK_StartIf:
674-
if (IfCount == 0)
675-
IfStartTok = HashTok;
676-
677-
++IfCount;
678-
continue;
679-
680-
case PDK_EndIf:
681-
// Mismatched #endif. The preamble ends here.
682-
if (IfCount == 0)
683-
break;
684-
685-
--IfCount;
686-
continue;
687-
688669
case PDK_Unknown:
689670
// We don't know what this directive is; stop at the '#'.
690671
break;
@@ -705,16 +686,13 @@ std::pair<unsigned, bool> Lexer::ComputePreamble(StringRef Buffer,
705686
} while (true);
706687

707688
SourceLocation End;
708-
if (IfCount)
709-
End = IfStartTok.getLocation();
710-
else if (ActiveCommentLoc.isValid())
689+
if (ActiveCommentLoc.isValid())
711690
End = ActiveCommentLoc; // don't truncate a decl comment.
712691
else
713692
End = TheTok.getLocation();
714693

715694
return std::make_pair(End.getRawEncoding() - StartLoc.getRawEncoding(),
716-
IfCount? IfStartTok.isAtStartOfLine()
717-
: TheTok.isAtStartOfLine());
695+
TheTok.isAtStartOfLine());
718696
}
719697

720698
/// AdvanceToTokenCharacter - Given a location that specifies the start of a
@@ -2570,6 +2548,11 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
25702548
return true;
25712549
}
25722550

2551+
if (PP->isRecordingPreamble() && !PP->isInMainFile()) {
2552+
PP->setRecordedPreambleConditionalStack(ConditionalStack);
2553+
ConditionalStack.clear();
2554+
}
2555+
25732556
// Issue diagnostics for unterminated #if and missing newline.
25742557

25752558
// If we are in a #if directive, emit an error.

Diff for: lib/Lex/PPLexerChange.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ bool Preprocessor::isInPrimaryFile() const {
4646
});
4747
}
4848

49+
bool Preprocessor::isInMainFile() const {
50+
if (IsFileLexer())
51+
return IncludeMacroStack.size() == 0;
52+
return true;
53+
}
54+
4955
/// getCurrentLexer - Return the current file lexer being lexed from. Note
5056
/// that this ignores any potentially active macro expansions and _Pragma
5157
/// expansions going on at the time.

Diff for: lib/Lex/Preprocessor.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
150150
Ident_GetExceptionInfo = Ident_GetExceptionCode = nullptr;
151151
Ident_AbnormalTermination = nullptr;
152152
}
153+
154+
if (this->PPOpts->GeneratePreamble)
155+
PreambleConditionalStack.startRecording();
153156
}
154157

155158
Preprocessor::~Preprocessor() {
@@ -532,6 +535,12 @@ void Preprocessor::EnterMainSourceFile() {
532535

533536
// Start parsing the predefines.
534537
EnterSourceFile(FID, nullptr, SourceLocation());
538+
539+
// Restore the conditional stack from the preamble, if there is one.
540+
if (PreambleConditionalStack.isReplaying()) {
541+
CurPPLexer->setConditionalLevels(PreambleConditionalStack.getStack());
542+
PreambleConditionalStack.doneReplaying();
543+
}
535544
}
536545

537546
void Preprocessor::EndSourceFile() {

Diff for: lib/Serialization/ASTReader.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -2925,6 +2925,21 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
29252925
}
29262926
break;
29272927

2928+
case PP_CONDITIONAL_STACK:
2929+
if (!Record.empty()) {
2930+
SmallVector<PPConditionalInfo, 4> ConditionalStack;
2931+
for (unsigned Idx = 0, N = Record.size() - 1; Idx < N; /* in loop */) {
2932+
auto Loc = ReadSourceLocation(F, Record, Idx);
2933+
bool WasSkipping = Record[Idx++];
2934+
bool FoundNonSkip = Record[Idx++];
2935+
bool FoundElse = Record[Idx++];
2936+
ConditionalStack.push_back(
2937+
{Loc, WasSkipping, FoundNonSkip, FoundElse});
2938+
}
2939+
PP.setReplayablePreambleConditionalStack(ConditionalStack);
2940+
}
2941+
break;
2942+
29282943
case PP_COUNTER_VALUE:
29292944
if (!Record.empty() && Listener)
29302945
Listener->ReadCounter(F, Record[0]);

Diff for: lib/Serialization/ASTWriter.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,7 @@ void ASTWriter::WriteBlockInfoBlock() {
10931093
RECORD(UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES);
10941094
RECORD(DELETE_EXPRS_TO_ANALYZE);
10951095
RECORD(CUDA_PRAGMA_FORCE_HOST_DEVICE_DEPTH);
1096+
RECORD(PP_CONDITIONAL_STACK);
10961097

10971098
// SourceManager Block.
10981099
BLOCK(SOURCE_MANAGER_BLOCK);
@@ -2302,6 +2303,18 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
23022303
Stream.EmitRecord(PP_COUNTER_VALUE, Record);
23032304
}
23042305

2306+
if (PP.isRecordingPreamble() && PP.hasRecordedPreamble()) {
2307+
assert(!IsModule);
2308+
for (const auto &Cond : PP.getPreambleConditionalStack()) {
2309+
AddSourceLocation(Cond.IfLoc, Record);
2310+
Record.push_back(Cond.WasSkipping);
2311+
Record.push_back(Cond.FoundNonSkip);
2312+
Record.push_back(Cond.FoundElse);
2313+
}
2314+
Stream.EmitRecord(PP_CONDITIONAL_STACK, Record);
2315+
Record.clear();
2316+
}
2317+
23052318
// Enter the preprocessor block.
23062319
Stream.EnterSubblock(PREPROCESSOR_BLOCK_ID, 3);
23072320

Diff for: test/Lexer/preamble.c

+5-6
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@
99
#pragma unknown
1010
#endif
1111
#ifdef WIBBLE
12-
#include "honk"
13-
#else
14-
int foo();
12+
#include "foo"
13+
int bar;
1514
#endif
1615

1716
// This test checks for detection of the preamble of a file, which
18-
// includes all of the starting comments and #includes. Note that any
19-
// changes to the preamble part of this file must be mirrored in
20-
// Inputs/preamble.txt, since we diff against it.
17+
// includes all of the starting comments and #includes.
2118

2219
// RUN: %clang_cc1 -print-preamble %s > %t
2320
// RUN: echo END. >> %t
@@ -33,4 +30,6 @@ int foo();
3330
// CHECK-NEXT: #endif
3431
// CHECK-NEXT: #pragma unknown
3532
// CHECK-NEXT: #endif
33+
// CHECK-NEXT: #ifdef WIBBLE
34+
// CHECK-NEXT: #include "foo"
3635
// CHECK-NEXT: END.

Diff for: test/Lexer/preamble2.c

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Preamble detection test: header with an include guard.
2+
#ifndef HEADER_H
3+
#define HEADER_H
4+
#include "foo"
5+
int bar;
6+
#endif
7+
8+
// This test checks for detection of the preamble of a file, which
9+
// includes all of the starting comments and #includes.
10+
11+
// RUN: %clang_cc1 -print-preamble %s > %t
12+
// RUN: echo END. >> %t
13+
// RUN: FileCheck < %t %s
14+
15+
// CHECK: // Preamble detection test: header with an include guard.
16+
// CHECK-NEXT: #ifndef HEADER_H
17+
// CHECK-NEXT: #define HEADER_H
18+
// CHECK-NEXT: #include "foo"
19+
// CHECK-NEXT: END.

0 commit comments

Comments
 (0)