aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Lex/Preprocessor.h3
-rw-r--r--lib/Lex/MacroArgs.cpp5
-rw-r--r--lib/Lex/Pragma.cpp65
-rw-r--r--lib/Lex/Preprocessor.cpp1
-rw-r--r--test/Preprocessor/_Pragma-in-macro-arg.c35
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;