Fix for expanding __pragmas in macro arguments

Summary:
Avoid parsing __pragma into an annotation token when macro arguments are pre-expanded.
This is what clang currently does when parsing _Pragmas.

Fixes https://bugs.llvm.org/show_bug.cgi?id=41128, where clang crashed
when trying to get the length of an annotation token.

Subscribers: cfe-commits

Tags: #clang

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

llvm-svn: 373950
This commit is contained in:
Amy Huang 2019-10-07 19:41:53 +00:00
parent be52ff9506
commit 6bdfe3aeba
2 changed files with 58 additions and 33 deletions

View File

@ -121,6 +121,40 @@ void PragmaNamespace::HandlePragma(Preprocessor &PP,
// Preprocessor Pragma Directive Handling.
//===----------------------------------------------------------------------===//
namespace {
// TokenCollector provides the option to collect tokens that were "read"
// and return them to the stream to be read later.
// Currently used when reading _Pragma/__pragma directives.
struct TokenCollector {
Preprocessor &Self;
bool Collect;
SmallVector<Token, 3> Tokens;
Token &Tok;
void lex() {
if (Collect)
Tokens.push_back(Tok);
Self.Lex(Tok);
}
void revert() {
assert(Collect && "did not collect tokens");
assert(!Tokens.empty() && "collected unexpected number of tokens");
// Push the ( "string" ) tokens into the token stream.
auto Toks = std::make_unique<Token[]>(Tokens.size());
std::copy(Tokens.begin() + 1, Tokens.end(), Toks.get());
Toks[Tokens.size() - 1] = Tok;
Self.EnterTokenStream(std::move(Toks), Tokens.size(),
/*DisableMacroExpansion*/ true,
/*IsReinject*/ true);
// ... and return the pragma token unchanged.
Tok = *Tokens.begin();
}
};
} // namespace
/// HandlePragmaDirective - The "\#pragma" directive has been parsed. Lex the
/// rest of the pragma, passing it to the registered pragma handlers.
void Preprocessor::HandlePragmaDirective(PragmaIntroducer Introducer) {
@ -166,35 +200,6 @@ void Preprocessor::Handle_Pragma(Token &Tok) {
// In Case #2, we check the syntax now, but then put the tokens back into the
// token stream for later consumption.
struct TokenCollector {
Preprocessor &Self;
bool Collect;
SmallVector<Token, 3> Tokens;
Token &Tok;
void lex() {
if (Collect)
Tokens.push_back(Tok);
Self.Lex(Tok);
}
void revert() {
assert(Collect && "did not collect tokens");
assert(!Tokens.empty() && "collected unexpected number of tokens");
// Push the ( "string" ) tokens into the token stream.
auto Toks = std::make_unique<Token[]>(Tokens.size());
std::copy(Tokens.begin() + 1, Tokens.end(), Toks.get());
Toks[Tokens.size() - 1] = Tok;
Self.EnterTokenStream(std::move(Toks), Tokens.size(),
/*DisableMacroExpansion*/ true,
/*IsReinject*/ true);
// ... and return the _Pragma token unchanged.
Tok = *Tokens.begin();
}
};
TokenCollector Toks = {*this, InMacroArgPreExpansion, {}, Tok};
// Remember the pragma token location.
@ -328,11 +333,15 @@ void Preprocessor::Handle_Pragma(Token &Tok) {
/// HandleMicrosoft__pragma - Like Handle_Pragma except the pragma text
/// is not enclosed within a string literal.
void Preprocessor::HandleMicrosoft__pragma(Token &Tok) {
// During macro pre-expansion, check the syntax now but put the tokens back
// into the token stream for later consumption. Same as Handle_Pragma.
TokenCollector Toks = {*this, InMacroArgPreExpansion, {}, Tok};
// Remember the pragma token location.
SourceLocation PragmaLoc = Tok.getLocation();
// Read the '('.
Lex(Tok);
Toks.lex();
if (Tok.isNot(tok::l_paren)) {
Diag(PragmaLoc, diag::err__Pragma_malformed);
return;
@ -341,14 +350,14 @@ void Preprocessor::HandleMicrosoft__pragma(Token &Tok) {
// Get the tokens enclosed within the __pragma(), as well as the final ')'.
SmallVector<Token, 32> PragmaToks;
int NumParens = 0;
Lex(Tok);
Toks.lex();
while (Tok.isNot(tok::eof)) {
PragmaToks.push_back(Tok);
if (Tok.is(tok::l_paren))
NumParens++;
else if (Tok.is(tok::r_paren) && NumParens-- == 0)
break;
Lex(Tok);
Toks.lex();
}
if (Tok.is(tok::eof)) {
@ -356,6 +365,12 @@ void Preprocessor::HandleMicrosoft__pragma(Token &Tok) {
return;
}
// If we're expanding a macro argument, put the tokens back.
if (InMacroArgPreExpansion) {
Toks.revert();
return;
}
PragmaToks.front().setFlag(Token::LeadingSpace);
// Replace the ')' with an EOD to mark the end of the pragma.

View File

@ -51,6 +51,8 @@ __pragma(comment(linker," bar=" BAR))
__pragma(warning(pop)); \
}
#define PRAGMA_IN_ARGS(p) p
void f()
{
__pragma() // expected-warning{{unknown pragma ignored}}
@ -64,8 +66,16 @@ void f()
// CHECK: #pragma warning(disable: 10000)
// CHECK: ; 1 + (2 > 3) ? 4 : 5;
// CHECK: #pragma warning(pop)
}
// Check that macro arguments can contain __pragma.
PRAGMA_IN_ARGS(MACRO_WITH__PRAGMA) // expected-warning {{lower precedence}} \
// expected-note 2 {{place parentheses}} \
// expected-warning {{expression result unused}}
// CHECK: #pragma warning(push)
// CHECK: #pragma warning(disable: 10000)
// CHECK: ; 1 + (2 > 3) ? 4 : 5;
// CHECK: #pragma warning(pop)
}
// This should include macro_arg_directive even though the include
// is looking for test.h This allows us to assign to "n"