diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -121,6 +121,40 @@ // 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 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(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 @@ // 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 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(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 @@ /// 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 @@ // Get the tokens enclosed within the __pragma(), as well as the final ')'. SmallVector 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 @@ 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. diff --git a/clang/test/Preprocessor/pragma_microsoft.c b/clang/test/Preprocessor/pragma_microsoft.c --- a/clang/test/Preprocessor/pragma_microsoft.c +++ b/clang/test/Preprocessor/pragma_microsoft.c @@ -51,6 +51,8 @@ __pragma(warning(pop)); \ } +#define PRAGMA_IN_ARGS(p) p + void f() { __pragma() // expected-warning{{unknown pragma ignored}} @@ -64,8 +66,16 @@ // 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"