Skip to content

Commit 24a1bed

Browse files
committedFeb 24, 2017
[Preprocessor] Fix incorrect token caching that occurs when lexing _Pragma
in macro argument pre-expansion mode when skipping a function body This commit fixes a token caching problem that currently occurs when clang is skipping a function body (e.g. when looking for a code completion token) and at the same time caching the tokens for _Pragma when lexing it in macro argument pre-expansion mode. When _Pragma is being lexed in macro argument pre-expansion mode, it caches the tokens so that it can avoid interpreting the pragma immediately (as the macro argument may not be used in the macro body), and then either backtracks over or commits these tokens. The problem is that, when we're backtracking/committing in such a scenario, there's already a previous backtracking position stored in BacktrackPositions (as we're skipping the function body), and this leads to a situation where the cached tokens from the pragma (like '(' 'string_literal' and ')') will remain in the cached tokens array incorrectly even after they're consumed (in the case of backtracking) or just ignored (in the case when they're committed). Furthermore, what makes it even worse, is that because of a previous backtracking position, the logic that deals with when should we call ExitCachingLexMode in CachingLex no longer works for us in this situation, and more tokens in the macro argument get cached, to the point where the EOF token that corresponds to the macro argument EOF is cached. This problem leads to all sorts of issues in code completion mode, where incorrect errors get presented and code completion completely fails to produce completion results. rdar://28523863 Differential Revision: https://reviews.llvm.org/D28772 llvm-svn: 296140
1 parent d030291 commit 24a1bed

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed
 

‎clang/include/clang/Lex/Preprocessor.h

+18
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,24 @@ class Preprocessor {
10771077
/// \brief Disable the last EnableBacktrackAtThisPos call.
10781078
void CommitBacktrackedTokens();
10791079

1080+
struct CachedTokensRange {
1081+
CachedTokensTy::size_type Begin, End;
1082+
};
1083+
1084+
private:
1085+
/// \brief A range of cached tokens that should be erased after lexing
1086+
/// when backtracking requires the erasure of such cached tokens.
1087+
Optional<CachedTokensRange> CachedTokenRangeToErase;
1088+
1089+
public:
1090+
/// \brief Returns the range of cached tokens that were lexed since
1091+
/// EnableBacktrackAtThisPos() was previously called.
1092+
CachedTokensRange LastCachedTokenRange();
1093+
1094+
/// \brief Erase the range of cached tokens that were lexed since
1095+
/// EnableBacktrackAtThisPos() was previously called.
1096+
void EraseCachedTokens(CachedTokensRange TokenRange);
1097+
10801098
/// \brief Make Preprocessor re-lex the tokens that were lexed since
10811099
/// EnableBacktrackAtThisPos() was previously called.
10821100
void Backtrack();

‎clang/lib/Lex/PPCaching.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,29 @@ void Preprocessor::CommitBacktrackedTokens() {
3535
BacktrackPositions.pop_back();
3636
}
3737

38+
Preprocessor::CachedTokensRange Preprocessor::LastCachedTokenRange() {
39+
assert(isBacktrackEnabled());
40+
auto PrevCachedLexPos = BacktrackPositions.back();
41+
return CachedTokensRange{PrevCachedLexPos, CachedLexPos};
42+
}
43+
44+
void Preprocessor::EraseCachedTokens(CachedTokensRange TokenRange) {
45+
assert(TokenRange.Begin <= TokenRange.End);
46+
if (CachedLexPos == TokenRange.Begin && TokenRange.Begin != TokenRange.End) {
47+
// We have backtracked to the start of the token range as we want to consume
48+
// them again. Erase the tokens only after consuming then.
49+
assert(!CachedTokenRangeToErase);
50+
CachedTokenRangeToErase = TokenRange;
51+
return;
52+
}
53+
// The cached tokens were committed, so they should be erased now.
54+
assert(TokenRange.End == CachedLexPos);
55+
CachedTokens.erase(CachedTokens.begin() + TokenRange.Begin,
56+
CachedTokens.begin() + TokenRange.End);
57+
CachedLexPos = TokenRange.Begin;
58+
ExitCachingLexMode();
59+
}
60+
3861
// Make Preprocessor re-lex the tokens that were lexed since
3962
// EnableBacktrackAtThisPos() was previously called.
4063
void Preprocessor::Backtrack() {
@@ -51,6 +74,13 @@ void Preprocessor::CachingLex(Token &Result) {
5174

5275
if (CachedLexPos < CachedTokens.size()) {
5376
Result = CachedTokens[CachedLexPos++];
77+
// Erase the some of the cached tokens after they are consumed when
78+
// asked to do so.
79+
if (CachedTokenRangeToErase &&
80+
CachedTokenRangeToErase->End == CachedLexPos) {
81+
EraseCachedTokens(*CachedTokenRangeToErase);
82+
CachedTokenRangeToErase = None;
83+
}
5484
return;
5585
}
5686

‎clang/lib/Lex/Pragma.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,23 @@ class LexingFor_PragmaRAII {
160160

161161
~LexingFor_PragmaRAII() {
162162
if (InMacroArgPreExpansion) {
163+
// When committing/backtracking the cached pragma tokens in a macro
164+
// argument pre-expansion we want to ensure that either the tokens which
165+
// have been committed will be removed from the cache or that the tokens
166+
// over which we just backtracked won't remain in the cache after they're
167+
// consumed and that the caching will stop after consuming them.
168+
// Otherwise the caching will interfere with the way macro expansion
169+
// works, because we will continue to cache tokens after consuming the
170+
// backtracked tokens, which shouldn't happen when we're dealing with
171+
// macro argument pre-expansion.
172+
auto CachedTokenRange = PP.LastCachedTokenRange();
163173
if (Failed) {
164174
PP.CommitBacktrackedTokens();
165175
} else {
166176
PP.Backtrack();
167177
OutTok = PragmaTok;
168178
}
179+
PP.EraseCachedTokens(CachedTokenRange);
169180
}
170181
}
171182

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
#define Outer(action) action
3+
4+
void completeParam(int param) {
5+
;
6+
Outer(__extension__({ _Pragma("clang diagnostic push") }));
7+
param;
8+
}
9+
10+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:7:1 %s | FileCheck %s
11+
// CHECK: param : [#int#]param
12+
13+
void completeParamPragmaError(int param) {
14+
Outer(__extension__({ _Pragma(2) })); // expected-error {{_Pragma takes a parenthesized string literal}}
15+
param;
16+
}
17+
18+
// RUN: %clang_cc1 -fsyntax-only -verify -code-completion-at=%s:16:1 %s | FileCheck %s

0 commit comments

Comments
 (0)
Please sign in to comment.