]> granicus.if.org Git - clang/commitdiff
Correct handling of _Pragma macro inside a macro argument.
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Tue, 3 Apr 2012 16:47:40 +0000 (16:47 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Tue, 3 Apr 2012 16:47:40 +0000 (16:47 +0000)
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

include/clang/Lex/Preprocessor.h
lib/Lex/MacroArgs.cpp
lib/Lex/Pragma.cpp
lib/Lex/Preprocessor.cpp
test/Preprocessor/_Pragma-in-macro-arg.c [new file with mode: 0644]

index 591a4498eb8dfc8d69dd5244aa2cba985474a7a9..055008fd44ce65d6a02e88c51d81330ce4c62c02 100644 (file)
@@ -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;
index d9a96a2890dc24a2d8fbe1bc8c32af79747f9e14..e2b251a4499da82f721049d451a1200bf9f31d98 100644 (file)
@@ -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;
 }
index 5d65cc4f23b125161032128b803012ef33e6d73f..e2a192b01f287c38eed37aff7fe8942724335595 100644 (file)
@@ -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,
index f7f63ee01fbf3a767dbf6a2dbadc6f284f3c475e..06e5685a8b674da3b3aeccf29e4eee1900f44346 100644 (file)
@@ -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 (file)
index 0000000..99a33a9
--- /dev/null
@@ -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;