diff options
-rw-r--r-- | include/clang/Lex/Preprocessor.h | 3 | ||||
-rw-r--r-- | lib/Lex/MacroArgs.cpp | 5 | ||||
-rw-r--r-- | lib/Lex/Pragma.cpp | 65 | ||||
-rw-r--r-- | lib/Lex/Preprocessor.cpp | 1 | ||||
-rw-r--r-- | test/Preprocessor/_Pragma-in-macro-arg.c | 35 |
5 files changed, 104 insertions, 5 deletions
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<Preprocessor> { /// \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 <algorithm> using namespace clang; @@ -155,6 +156,8 @@ MacroArgs::getPreExpArgument(unsigned Arg, const MacroInfo *MI, std::vector<Token> &Result = PreExpArgTokens[Arg]; if (!Result.empty()) return Result; + SaveAndRestore<bool> 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; |