From: Argyrios Kyrtzidis Date: Tue, 3 Apr 2012 16:47:40 +0000 (+0000) Subject: Correct handling of _Pragma macro inside a macro argument. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=14e645557ae91c6770d62beb00a1c522e0bfd5d6;p=clang Correct handling of _Pragma macro inside a macro argument. If we are pre-expanding a macro argument don't actually "activate" the pragma at that point, activate the pragma whenever we encounter it again in the token stream. This ensures that we will activate it in the correct location or that we will ignore it if it never enters the token stream, e.g: \#define EMPTY(x) \#define INACTIVE(x) EMPTY(x) INACTIVE(_Pragma("clang diagnostic ignored \"-Wconversion\"")) This also fixes the crash in rdar://11168596. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153959 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 591a4498eb..055008fd44 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -124,6 +124,9 @@ class Preprocessor : public RefCountedBase { /// \brief Whether we have already loaded macros from the external source. mutable bool ReadMacrosFromExternalSource : 1; + /// \brief True if we are pre-expanding macro arguments. + bool InMacroArgPreExpansion; + /// Identifiers - This is mapping/lookup information for all identifiers in /// the program, including program keywords. mutable IdentifierTable Identifiers; diff --git a/lib/Lex/MacroArgs.cpp b/lib/Lex/MacroArgs.cpp index d9a96a2890..e2b251a449 100644 --- a/lib/Lex/MacroArgs.cpp +++ b/lib/Lex/MacroArgs.cpp @@ -16,6 +16,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Lex/LexDiagnostic.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/SaveAndRestore.h" #include using namespace clang; @@ -155,6 +156,8 @@ MacroArgs::getPreExpArgument(unsigned Arg, const MacroInfo *MI, std::vector &Result = PreExpArgTokens[Arg]; if (!Result.empty()) return Result; + SaveAndRestore PreExpandingMacroArgs(PP.InMacroArgPreExpansion, true); + const Token *AT = getUnexpArgument(Arg); unsigned NumToks = getArgLength(AT)+1; // Include the EOF. @@ -177,6 +180,8 @@ MacroArgs::getPreExpArgument(unsigned Arg, const MacroInfo *MI, // will not otherwise be popped until the next token is lexed. The problem is // that the token may be lexed sometime after the vector of tokens itself is // destroyed, which would be badness. + if (PP.InCachingLexMode()) + PP.ExitCachingLexMode(); PP.RemoveTopOfLexerStack(); return Result; } diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index 5d65cc4f23..e2a192b01f 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -115,10 +115,61 @@ void Preprocessor::HandlePragmaDirective(unsigned Introducer) { DiscardUntilEndOfDirective(); } +namespace { +/// \brief Helper class for \see Preprocessor::Handle_Pragma. +class LexingFor_PragmaRAII { + Preprocessor &PP; + bool InMacroArgPreExpansion; + bool Failed; + Token &OutTok; + Token PragmaTok; + +public: + LexingFor_PragmaRAII(Preprocessor &PP, bool InMacroArgPreExpansion, + Token &Tok) + : PP(PP), InMacroArgPreExpansion(InMacroArgPreExpansion), + Failed(false), OutTok(Tok) { + if (InMacroArgPreExpansion) { + PragmaTok = OutTok; + PP.EnableBacktrackAtThisPos(); + } + } + + ~LexingFor_PragmaRAII() { + if (InMacroArgPreExpansion) { + if (Failed) { + PP.CommitBacktrackedTokens(); + } else { + PP.Backtrack(); + OutTok = PragmaTok; + } + } + } + + void failed() { + Failed = true; + } +}; +} + /// Handle_Pragma - Read a _Pragma directive, slice it up, process it, then /// return the first token after the directive. The _Pragma token has just /// been read into 'Tok'. void Preprocessor::Handle_Pragma(Token &Tok) { + + // This works differently if we are pre-expanding a macro argument. + // In that case we don't actually "activate" the pragma now, we only lex it + // until we are sure it is lexically correct and then we backtrack so that + // we activate the pragma whenever we encounter the tokens again in the token + // stream. This ensures that we will activate it in the correct location + // or that we will ignore it if it never enters the token stream, e.g: + // + // #define EMPTY(x) + // #define INACTIVE(x) EMPTY(x) + // INACTIVE(_Pragma("clang diagnostic ignored \"-Wconversion\"")) + + LexingFor_PragmaRAII _PragmaLexing(*this, InMacroArgPreExpansion, Tok); + // Remember the pragma token location. SourceLocation PragmaLoc = Tok.getLocation(); @@ -126,7 +177,7 @@ void Preprocessor::Handle_Pragma(Token &Tok) { Lex(Tok); if (Tok.isNot(tok::l_paren)) { Diag(PragmaLoc, diag::err__Pragma_malformed); - return; + return _PragmaLexing.failed(); } // Read the '"..."'. @@ -138,7 +189,7 @@ void Preprocessor::Handle_Pragma(Token &Tok) { Lex(Tok); if (Tok.is(tok::r_paren)) Lex(Tok); - return; + return _PragmaLexing.failed(); } if (Tok.hasUDSuffix()) { @@ -147,20 +198,24 @@ void Preprocessor::Handle_Pragma(Token &Tok) { Lex(Tok); if (Tok.is(tok::r_paren)) Lex(Tok); - return; + return _PragmaLexing.failed(); } // Remember the string. - std::string StrVal = getSpelling(Tok); + Token StrTok = Tok; // Read the ')'. Lex(Tok); if (Tok.isNot(tok::r_paren)) { Diag(PragmaLoc, diag::err__Pragma_malformed); - return; + return _PragmaLexing.failed(); } + if (InMacroArgPreExpansion) + return; + SourceLocation RParenLoc = Tok.getLocation(); + std::string StrVal = getSpelling(StrTok); // The _Pragma is lexically sound. Destringize according to C99 6.10.9.1: // "The string literal is destringized by deleting the L prefix, if present, diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index f7f63ee01f..06e5685a8b 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -135,6 +135,7 @@ void Preprocessor::Initialize(const TargetInfo &Target) { // Macro expansion is enabled. DisableMacroExpansion = false; InMacroArgs = false; + InMacroArgPreExpansion = false; NumCachedTokenLexers = 0; CachedLexPos = 0; diff --git a/test/Preprocessor/_Pragma-in-macro-arg.c b/test/Preprocessor/_Pragma-in-macro-arg.c new file mode 100644 index 0000000000..99a33a9e1b --- /dev/null +++ b/test/Preprocessor/_Pragma-in-macro-arg.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 %s -verify -Wconversion + +// Don't crash (rdar://11168596) +#define A(desc) _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wparentheses\"") _Pragma("clang diagnostic pop") +#define B(desc) A(desc) +B(_Pragma("clang diagnostic ignored \"-Wparentheses\"")) + + +#define EMPTY(x) +#define INACTIVE(x) EMPTY(x) + +#define ID(x) x +#define ACTIVE(x) ID(x) + +// This should be ignored.. +INACTIVE(_Pragma("clang diagnostic ignored \"-Wconversion\"")) + +#define IGNORE_CONV _Pragma("clang diagnostic ignored \"-Wconversion\"") + +// ..as should this. +INACTIVE(IGNORE_CONV) + +#define IGNORE_POPPUSH(Pop, Push, W, D) Push W D Pop +IGNORE_POPPUSH(_Pragma("clang diagnostic pop"), _Pragma("clang diagnostic push"), + _Pragma("clang diagnostic ignored \"-Wconversion\""), int q = (double)1.0); + +int x1 = (double)1.0; // expected-warning {{implicit conversion}} + +ACTIVE(_Pragma) ("clang diagnostic ignored \"-Wconversion\"")) // expected-error {{_Pragma takes a parenthesized string literal}} \ + expected-error {{expected identifier or '('}} expected-error {{expected ')'}} expected-note {{to match this '('}} + +// This should disable the warning. +ACTIVE(IGNORE_CONV) + +int x2 = (double)1.0;