https://github.com/ChenMiaoi created
https://github.com/llvm/llvm-project/pull/230927
Nested `_Pragma` or Microsoft `__pragma` expressions can produce annotation
tokens in an enclosing pragma's arguments. This patch fixes crashes when these
tokens are processed as arguments or used to construct diagnostics, and
improves error recovery for nested pragmas.
``` cc
#pragma vtordisp(push, _Pragma("weak foobar"))
```
After the inner `_Pragma` is processed, the enclosing pragma receives an
annotation where it expects a mode argument. Similar cases occur when arguments
are buffered and parsed later.
Previously, these paths called `getIdentifierInfo()` and then checked whether
the result was null. The assertion occurs inside the getter, so checking its
return value cannot prevent the crash.
This patch checks the token kind before calling the getter. Only identifiers or
keywords permitted by the pragma's syntax are queried for identifier
information; other tokens follow the existing diagnostic and recovery paths.
Split by #228990
>From 09394ace8975bf78ab78b364067595e92db7f540 Mon Sep 17 00:00:00 2001
From: Chen Miao <[email protected]>
Date: Sun, 11 Oct 2026 15:16:17 +0800
Subject: [PATCH] [clang] Guard identifier reads in pragma handlers
---
clang/docs/ReleaseNotes.md | 3 +
clang/lib/Lex/Pragma.cpp | 43 +++++++---
clang/lib/Parse/ParsePragma.cpp | 62 ++++++++------
clang/test/FixIt/fixit-pragma-attribute.cpp | 5 ++
clang/test/Parser/pragma-attribute.cpp | 7 ++
.../Parser/pragma-nested-annotation-target.c | 31 +++++++
clang/test/Parser/pragma-nested-annotation.c | 85 +++++++++++++++++++
clang/unittests/Lex/LexerTest.cpp | 25 ++++++
8 files changed, 224 insertions(+), 37 deletions(-)
create mode 100644 clang/test/Parser/pragma-nested-annotation-target.c
create mode 100644 clang/test/Parser/pragma-nested-annotation.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 77a1e73bc8af6..4867f40c51cae 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -947,6 +947,9 @@ features cannot lower the translation-unit ABI level;
#### Miscellaneous Clang Crashes Fixed
+- Fixed crashes when nested `_Pragma` or Microsoft `__pragma` expressions
+ introduce annotation tokens where a pragma expects an identifier. (#GH225035)
+
- Fixed a crash when Microsoft extensions were enabled and an unterminated
`__identifier` expression reached the end of a preprocessing directive or
source file. (#GH222310)
diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp
index 078842ab08c5e..e47bbc7402ef5 100644
--- a/clang/lib/Lex/Pragma.cpp
+++ b/clang/lib/Lex/Pragma.cpp
@@ -108,10 +108,11 @@ void PragmaNamespace::HandlePragma(Preprocessor &PP,
PP.LexUnexpandedToken(Tok);
// Get the handler for this token. If there is no handler, ignore the
pragma.
- PragmaHandler *Handler
- = FindHandler(Tok.getIdentifierInfo() ? Tok.getIdentifierInfo()->getName()
- : StringRef(),
- /*IgnoreNull=*/false);
+ PragmaHandler *Handler = FindHandler(
+ Tok.is(tok::identifier) || tok::getKeywordSpelling(Tok.getKind())
+ ? Tok.getIdentifierInfo()->getName()
+ : StringRef(),
+ /*IgnoreNull=*/false);
if (!Handler) {
PP.Diag(Tok, diag::warn_pragma_ignored);
return;
@@ -921,7 +922,7 @@ void Preprocessor::HandlePragmaSetPPState(PragmaIntroducer
Introducer,
Token &Tok) {
// Lex the macro name we want to set.
LexUnexpandedToken(Tok);
- if (!Tok.getIdentifierInfo()) {
+ if (Tok.isNot(tok::identifier) && !tok::getKeywordSpelling(Tok.getKind())) {
Diag(Tok.getLocation(), diag::err_pp_pragma_set_pp_state_expected_name);
return;
}
@@ -1149,7 +1150,10 @@ struct PragmaDebugHandler : public PragmaHandler {
} else if (II->isStr("macro")) {
Token MacroName;
PP.LexUnexpandedToken(MacroName);
- auto *MacroII = MacroName.getIdentifierInfo();
+ auto *MacroII = MacroName.is(tok::identifier) ||
+ tok::getKeywordSpelling(MacroName.getKind())
+ ? MacroName.getIdentifierInfo()
+ : nullptr;
if (MacroII)
PP.dumpMacroInfo(MacroII);
else
@@ -1174,7 +1178,10 @@ struct PragmaDebugHandler : public PragmaHandler {
} else if (II->isStr("module_lookup")) {
Token MName;
PP.LexUnexpandedToken(MName);
- auto *MNameII = MName.getIdentifierInfo();
+ auto *MNameII =
+ MName.is(tok::identifier) || tok::getKeywordSpelling(MName.getKind())
+ ? MName.getIdentifierInfo()
+ : nullptr;
if (!MNameII) {
PP.Diag(MName, diag::warn_pragma_debug_missing_argument)
<< II->getName();
@@ -1219,7 +1226,10 @@ struct PragmaDebugHandler : public PragmaHandler {
Token Kind;
PP.LexUnexpandedToken(Kind);
- auto *DumpII = Kind.getIdentifierInfo();
+ auto *DumpII =
+ Kind.is(tok::identifier) || tok::getKeywordSpelling(Kind.getKind())
+ ? Kind.getIdentifierInfo()
+ : nullptr;
if (!DumpII) {
PP.Diag(Kind, diag::warn_pragma_debug_missing_argument)
<< II->getName();
@@ -1453,7 +1463,9 @@ struct PragmaWarningHandler : public PragmaHandler {
}
PP.Lex(Tok);
- IdentifierInfo *II = Tok.getIdentifierInfo();
+ IdentifierInfo *II = Tok.isOneOf(tok::identifier, tok::kw_default)
+ ? Tok.getIdentifierInfo()
+ : nullptr;
if (II && II->isStr("push")) {
// #pragma warning( push[ ,n ] )
@@ -1484,7 +1496,9 @@ struct PragmaWarningHandler : public PragmaHandler {
// #pragma warning( warning-specifier : warning-number-list
// [; warning-specifier : warning-number-list...] )
while (true) {
- II = Tok.getIdentifierInfo();
+ II = Tok.isOneOf(tok::identifier, tok::kw_default)
+ ? Tok.getIdentifierInfo()
+ : nullptr;
if (!II && !Tok.is(tok::numeric_constant)) {
PP.Diag(Tok, diag::warn_pragma_warning_spec_invalid);
return;
@@ -1601,7 +1615,8 @@ struct PragmaExecCharsetHandler : public PragmaHandler {
}
PP.Lex(Tok);
- IdentifierInfo *II = Tok.getIdentifierInfo();
+ IdentifierInfo *II =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (II && II->isStr("push")) {
// #pragma execution_character_set( push[ , string ] )
@@ -1930,7 +1945,8 @@ struct PragmaARCCFCodeAuditedHandler : public
PragmaHandler {
// Lex the 'begin' or 'end'.
PP.LexUnexpandedToken(Tok);
- const IdentifierInfo *BeginEnd = Tok.getIdentifierInfo();
+ const IdentifierInfo *BeginEnd =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (BeginEnd && BeginEnd->isStr("begin")) {
IsBegin = true;
} else if (BeginEnd && BeginEnd->isStr("end")) {
@@ -1985,7 +2001,8 @@ struct PragmaAssumeNonNullHandler : public PragmaHandler {
// Lex the 'begin' or 'end'.
PP.LexUnexpandedToken(Tok);
- const IdentifierInfo *BeginEnd = Tok.getIdentifierInfo();
+ const IdentifierInfo *BeginEnd =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (BeginEnd && BeginEnd->isStr("begin")) {
IsBegin = true;
} else if (BeginEnd && BeginEnd->isStr("end")) {
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 04e7bb5a9b7dd..77c2f3e0b200e 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1310,7 +1310,8 @@ bool Parser::HandlePragmaMSStrictGuardStackCheck(
bool Value = false;
if (Action & Sema::PSK_Push || Action & Sema::PSK_Set) {
- const IdentifierInfo *II = Tok.getIdentifierInfo();
+ const IdentifierInfo *II =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (II && II->isStr("off")) {
PP.Lex(Tok);
Value = false;
@@ -1553,7 +1554,8 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
if (StateOption) {
ConsumeAnnotationToken();
SourceLocation StateLoc = Toks[0].getLocation();
- IdentifierInfo *StateInfo = Toks[0].getIdentifierInfo();
+ IdentifierInfo *StateInfo =
+ Toks[0].is(tok::identifier) ? Toks[0].getIdentifierInfo() : nullptr;
bool Valid =
StateInfo &&
@@ -1583,7 +1585,8 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
ConsumeAnnotationToken();
SourceLocation StateLoc = Toks[0].getLocation();
- IdentifierInfo *StateInfo = Toks[0].getIdentifierInfo();
+ IdentifierInfo *StateInfo =
+ Toks[0].is(tok::identifier) ? Toks[0].getIdentifierInfo() : nullptr;
StringRef IsScalableStr = StateInfo ? StateInfo->getName() : "";
// Look for vectorize_width(fixed|scalable)
@@ -1612,7 +1615,7 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
if (Tok.is(tok::comma)) {
PP.Lex(Tok); // ,
- StateInfo = Tok.getIdentifierInfo();
+ StateInfo = Tok.is(tok::identifier) ? Tok.getIdentifierInfo() :
nullptr;
IsScalableStr = StateInfo ? StateInfo->getName() : "";
if (IsScalableStr != "scalable" && IsScalableStr != "fixed") {
@@ -1860,7 +1863,8 @@ enum class MissingAttributeSubjectRulesRecoveryPoint {
MissingAttributeSubjectRulesRecoveryPoint
getAttributeSubjectRulesRecoveryPointForToken(const Token &Tok) {
- if (const auto *II = Tok.getIdentifierInfo()) {
+ if (Tok.is(tok::identifier)) {
+ const auto *II = Tok.getIdentifierInfo();
if (II->isStr("apply_to"))
return MissingAttributeSubjectRulesRecoveryPoint::ApplyTo;
if (II->isStr("any"))
@@ -2028,7 +2032,7 @@ void Parser::HandlePragmaAttribute() {
ParseMicrosoftDeclSpecs(Attrs);
} else {
Diag(Tok, diag::err_pragma_attribute_expected_attribute_syntax);
- if (Tok.getIdentifierInfo()) {
+ if (Tok.is(tok::identifier) || tok::getKeywordSpelling(Tok.getKind())) {
// If we suspect that this is an attribute suggest the use of
// '__attribute__'.
if (ParsedAttr::getParsedKind(
@@ -2141,7 +2145,8 @@ void
PragmaGCCVisibilityHandler::HandlePragma(Preprocessor &PP,
Token Tok;
PP.LexUnexpandedToken(Tok);
- const IdentifierInfo *PushPop = Tok.getIdentifierInfo();
+ const IdentifierInfo *PushPop =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
const IdentifierInfo *VisType;
if (PushPop && PushPop->isStr("pop")) {
@@ -2154,7 +2159,9 @@ void
PragmaGCCVisibilityHandler::HandlePragma(Preprocessor &PP,
return;
}
PP.LexUnexpandedToken(Tok);
- VisType = Tok.getIdentifierInfo();
+ VisType = Tok.isOneOf(tok::identifier, tok::kw_default, tok::kw_protected)
+ ? Tok.getIdentifierInfo()
+ : nullptr;
if (!VisType) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
<< "visibility";
@@ -2842,7 +2849,8 @@ void PragmaMSPointersToMembers::HandlePragma(Preprocessor
&PP,
return;
}
PP.Lex(Tok);
- const IdentifierInfo *Arg = Tok.getIdentifierInfo();
+ const IdentifierInfo *Arg =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (!Arg) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
<< "pointers_to_members";
@@ -2858,7 +2866,7 @@ void PragmaMSPointersToMembers::HandlePragma(Preprocessor
&PP,
if (Tok.is(tok::comma)) {
PP.Lex(Tok);
- Arg = Tok.getIdentifierInfo();
+ Arg = Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (!Arg) {
PP.Diag(Tok.getLocation(),
diag::err_pragma_pointers_to_members_unknown_kind)
@@ -2937,7 +2945,8 @@ void PragmaMSVtorDisp::HandlePragma(Preprocessor &PP,
PP.Lex(Tok);
Sema::PragmaMsStackAction Action = Sema::PSK_Set;
- const IdentifierInfo *II = Tok.getIdentifierInfo();
+ const IdentifierInfo *II =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (II) {
if (II->isStr("push")) {
// #pragma vtordisp(push, mode)
@@ -2965,7 +2974,8 @@ void PragmaMSVtorDisp::HandlePragma(Preprocessor &PP,
uint64_t Value = 0;
if (Action & Sema::PSK_Push || Action & Sema::PSK_Set) {
- const IdentifierInfo *II = Tok.getIdentifierInfo();
+ const IdentifierInfo *II =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (II && II->isStr("off")) {
PP.Lex(Tok);
Value = 0;
@@ -3338,7 +3348,7 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP,
}
if (Tok.isNot(tok::identifier)) {
PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_invalid_argument)
- << PP.getSpelling(Tok);
+ << PP.getSpelling(Tok);
return;
}
const IdentifierInfo *II = Tok.getIdentifierInfo();
@@ -3348,14 +3358,14 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor
&PP,
IsOn = true;
} else if (!II->isStr("off")) {
PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_invalid_argument)
- << PP.getSpelling(Tok);
+ << PP.getSpelling(Tok);
return;
}
PP.Lex(Tok);
if (Tok.isNot(tok::eod)) {
PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_extra_argument)
- << PP.getSpelling(Tok);
+ << PP.getSpelling(Tok);
return;
}
@@ -3883,11 +3893,12 @@ bool Parser::HandlePragmaMSOptimize(StringRef
PragmaName,
<< PragmaName << /*Expected=*/true << "'on' or 'off'";
return false;
}
- IdentifierInfo *II = Tok.getIdentifierInfo();
+ IdentifierInfo *II =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (!II || (!II->isStr("on") && !II->isStr("off"))) {
PP.Diag(PragmaLocation, diag::warn_pragma_invalid_argument)
- << PP.getSpelling(Tok) << PragmaName << /*Expected=*/true
- << "'on' or 'off'";
+ << (Tok.isAnnotation() ? "" : PP.getSpelling(Tok)) << PragmaName
+ << /*Expected=*/true << "'on' or 'off'";
return false;
}
bool IsOn = II->isStr("on");
@@ -3968,7 +3979,8 @@ void PragmaForceCUDAHostDeviceHandler::HandlePragma(
Token FirstTok = Tok;
PP.Lex(Tok);
- IdentifierInfo *Info = Tok.getIdentifierInfo();
+ IdentifierInfo *Info =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (!Info || (!Info->isStr("begin") && !Info->isStr("end"))) {
PP.Diag(FirstTok.getLocation(),
diag::warn_pragma_force_cuda_host_device_bad_arg);
@@ -4227,21 +4239,23 @@ void PragmaRISCVHandler::HandlePragma(Preprocessor &PP,
Token &FirstToken) {
Token Tok;
PP.Lex(Tok);
- IdentifierInfo *II = Tok.getIdentifierInfo();
+ IdentifierInfo *II =
+ Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (!II || !II->isStr("intrinsic")) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
- << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true <<
"'intrinsic'";
+ << (Tok.isAnnotation() ? "" : PP.getSpelling(Tok)) << "riscv"
+ << /*Expected=*/true << "'intrinsic'";
return;
}
PP.Lex(Tok);
- II = Tok.getIdentifierInfo();
+ II = Tok.is(tok::identifier) ? Tok.getIdentifierInfo() : nullptr;
if (!II || !(II->isStr("vector") || II->isStr("sifive_vector") ||
II->isStr("andes_vector"))) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
- << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true
- << "'vector', 'sifive_vector' or 'andes_vector'";
+ << (Tok.isAnnotation() ? "" : PP.getSpelling(Tok)) << "riscv"
+ << /*Expected=*/true << "'vector', 'sifive_vector' or 'andes_vector'";
return;
}
diff --git a/clang/test/FixIt/fixit-pragma-attribute.cpp
b/clang/test/FixIt/fixit-pragma-attribute.cpp
index 3824e45d8fca5..ac6e1b5a9041a 100644
--- a/clang/test/FixIt/fixit-pragma-attribute.cpp
+++ b/clang/test/FixIt/fixit-pragma-attribute.cpp
@@ -7,6 +7,11 @@
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:31-[[@LINE-1]]:31}:"__attribute__(("
// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:49-[[@LINE-2]]:49}:"))"
+#pragma clang attribute push(const, apply_to=function)
+// CHECK: note: use the GNU '__attribute__' syntax
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:30-[[@LINE-2]]:30}:"__attribute__(("
+// CHECK: fix-it:{{.*}}:{[[@LINE-3]]:36-[[@LINE-3]]:36}:"))"
+
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to =
any( enum, function, function, namespace, function ))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:97-[[@LINE-1]]:107}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:118-[[@LINE-2]]:127}:""
diff --git a/clang/test/Parser/pragma-attribute.cpp
b/clang/test/Parser/pragma-attribute.cpp
index 1f90a8990bb96..78699c06262cd 100644
--- a/clang/test/Parser/pragma-attribute.cpp
+++ b/clang/test/Parser/pragma-attribute.cpp
@@ -180,6 +180,13 @@ _Pragma("clang attribute pop");
#pragma clang attribute push (annotate("test")) // expected-error {{expected
an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
// expected-note@-1 {{use the GNU '__attribute__' syntax}}
+// Nested pragmas must be diagnosed without crashing (GH225035).
+#pragma clang attribute (_Pragma("clang attribute push")) // expected-error
{{expected an attribute that is specified using the GNU, C++11 or '__declspec'
syntax}}
+#pragma clang attribute push (_Pragma("clang attribute push")) //
expected-error {{expected an attribute that is specified using the GNU, C++11
or '__declspec' syntax}}
+#pragma clang attribute push(__attribute__((annotate("test"))) _Pragma("clang
attribute push")) // expected-error {{expected ','}}
+#pragma clang attribute push(__attribute__((annotate("test"))), _Pragma("clang
attribute push")) // expected-error {{expected attribute subject set specifier
'apply_to'}}
+#pragma clang attribute push(__attribute__((annotate("test"))), apply_to
_Pragma("clang attribute push")) // expected-error {{expected '='}}
+
#pragma clang attribute push([[clang::uninitialized]], apply_to =
variable(is_local))
#pragma clang attribute pop
#pragma clang attribute push([[clang::uninitialized]], apply_to = function) //
expected-error {{attribute 'clang::uninitialized' cannot be applied to
'function'}}
diff --git a/clang/test/Parser/pragma-nested-annotation-target.c
b/clang/test/Parser/pragma-nested-annotation-target.c
new file mode 100644
index 0000000000000..345a5b60ef7cc
--- /dev/null
+++ b/clang/test/Parser/pragma-nested-annotation-target.c
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple riscv64 -fsyntax-only -fms-extensions -DTEST_RISCV
-verify=riscv %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x cuda -fsyntax-only
-fms-extensions -DTEST_CUDA -verify=cuda %s
+
+// Existing recovery can leave inner pragma arguments in the token stream.
+// Use empty declarations to keep the invalid cases independent.
+
+#ifdef TEST_RISCV
+#pragma clang riscv _Pragma("weak foobar") intrinsic vector // riscv-warning
{{unexpected argument '' to '#pragma riscv'; expected 'intrinsic'}}
+// riscv-error@-1 {{unknown type name 'foobar'}} riscv-error@-1 {{expected ';'
after top level declarator}}
+;
+#pragma clang riscv intrinsic _Pragma("weak foobar") vector // riscv-warning
{{unexpected argument '' to '#pragma riscv'; expected 'vector', 'sifive_vector'
or 'andes_vector'}}
+// riscv-error@-1 {{unknown type name 'foobar'}} riscv-error@-1 {{expected ';'
after top level declarator}}
+;
+
+// Invalid ordinary tokens keep their spelling.
+#pragma clang riscv 42 // riscv-warning {{unexpected argument '42' to '#pragma
riscv'; expected 'intrinsic'}}
+#pragma clang riscv intrinsic wrong // riscv-warning {{unexpected argument
'wrong' to '#pragma riscv'; expected 'vector', 'sifive_vector' or
'andes_vector'}}
+#endif
+
+#ifdef TEST_CUDA
+#pragma clang force_cuda_host_device _Pragma("weak foobar") // cuda-warning
{{incorrect use of #pragma clang force_cuda_host_device begin|end}}
+// cuda-error@-1 {{unknown type name 'foobar'}} cuda-error@-1 {{expected
unqualified-id}}
+;
+
+#pragma clang force_cuda_host_device begin
+void host_device_function(void) {}
+#pragma clang force_cuda_host_device end
+#endif
+
+int after_pragmas;
+int use_after_pragmas(void) { return after_pragmas; }
diff --git a/clang/test/Parser/pragma-nested-annotation.c
b/clang/test/Parser/pragma-nested-annotation.c
new file mode 100644
index 0000000000000..aef659ca0475d
--- /dev/null
+++ b/clang/test/Parser/pragma-nested-annotation.c
@@ -0,0 +1,85 @@
+// RUN: %clang_cc1 -fsyntax-only -fms-extensions -verify=expected,c %s
+// RUN: %clang_cc1 -fsyntax-only -fms-extensions -fms-compatibility
-verify=expected,c %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -fms-extensions -verify=expected,cxx %s
+
+// Nested _Pragma annotations must be rejected without querying identifier
+// information on the annotation token. Existing recovery can leave
+// inner pragma arguments in the token stream; expect the resulting errors and
+// use empty declarations to keep the cases independent.
+#pragma strict_gs_check(push, _Pragma("pack(1)")) // expected-warning
{{unknown action for '#pragma strict_gs_check' - ignored}}
+#pragma optimize("", _Pragma("weak foobar")) // expected-warning {{unexpected
argument '' to '#pragma optimize'; expected 'on' or 'off'}}
+#pragma optimize("", 42) // expected-warning {{unexpected argument '42' to
'#pragma optimize'; expected 'on' or 'off'}}
+#pragma vtordisp(_Pragma("weak foobar")) // expected-warning {{unknown action
for '#pragma vtordisp' - ignored}}
+// c-error@-1 {{type specifier missing}} c-error@-1 {{expected ';' after top
level declarator}}
+// cxx-error@-2 {{unknown type name 'foobar'}} cxx-error@-2 {{expected
unqualified-id}}
+;
+#pragma vtordisp(push, _Pragma("weak foobar")) // expected-warning {{unknown
action for '#pragma vtordisp' - ignored}}
+// c-error@-1 {{type specifier missing}} c-error@-1 {{expected ';' after top
level declarator}}
+// cxx-error@-2 {{unknown type name 'foobar'}} cxx-error@-2 {{expected
unqualified-id}}
+;
+#pragma pointers_to_members(_Pragma("weak foobar")) // expected-warning
{{expected identifier in '#pragma pointers_to_members' - ignored}}
+// c-error@-1 {{type specifier missing}} c-error@-1 {{expected ';' after top
level declarator}}
+// cxx-error@-2 {{unknown type name 'foobar'}} cxx-error@-2 {{expected
unqualified-id}}
+;
+#pragma pointers_to_members(full_generality, _Pragma("weak foobar")) //
expected-error {{unexpected <annot_pragma_weak>, expected to see one of
'single_inheritance', 'multiple_inheritance', or 'virtual_inheritance'}}
+// c-error@-1 {{type specifier missing}} c-error@-1 {{expected ';' after top
level declarator}}
+// cxx-error@-2 {{unknown type name 'foobar'}} cxx-error@-2 {{expected
unqualified-id}}
+;
+#pragma warning(_Pragma("weak foobar")) // expected-warning {{#pragma warning
expected 'push', 'pop', 'default', 'disable', 'error', 'once', 'suppress', 1,
2, 3, or 4}}
+// c-error@-1 {{type specifier missing}} c-error@-1 {{expected ';' after top
level declarator}}
+// cxx-error@-2 {{unknown type name 'foobar'}} cxx-error@-2 {{expected
unqualified-id}}
+;
+#pragma warning(disable : 1; _Pragma("weak foobar")) // expected-warning
{{#pragma warning expected 'push', 'pop', 'default', 'disable', 'error',
'once', 'suppress', 1, 2, 3, or 4}}
+// c-error@-1 {{type specifier missing}} c-error@-1 {{expected ';' after top
level declarator}}
+// cxx-error@-2 {{unknown type name 'foobar'}} cxx-error@-2 {{expected
unqualified-id}}
+;
+#pragma execution_character_set(_Pragma("weak foobar")) // expected-warning
{{#pragma execution_character_set expected 'push' or 'pop'}}
+// c-error@-1 {{type specifier missing}} c-error@-1 {{expected ';' after top
level declarator}}
+// cxx-error@-2 {{unknown type name 'foobar'}} cxx-error@-2 {{expected
unqualified-id}}
+;
+
+void loop_state(void) {
+#pragma clang loop vectorize(_Pragma("weak foobar")) // expected-error
{{invalid argument; expected 'enable', 'assume_safety' or 'disable'}}
+ for (int i = 0; i < 4; ++i) {}
+}
+
+void loop_width(void) {
+#pragma clang loop vectorize_width(_Pragma("weak foobar")) // expected-error
{{expected expression}} expected-note {{vectorize_width loop hint malformed}}
expected-warning {{extra tokens at end of '#pragma clang loop vectorize_width'
- ignored}}
+ for (int i = 0; i < 4; ++i) {}
+}
+
+void loop_width_second_arg(void) {
+#pragma clang loop vectorize_width(4, _Pragma("weak foobar")) //
expected-error {{vectorize_width loop hint malformed}} expected-warning {{extra
tokens at end of '#pragma clang loop vectorize_width' - ignored}}
+ for (int i = 0; i < 4; ++i) {}
+}
+
+// Preserve the non-identifier alternatives in valid pragmas.
+#pragma vtordisp()
+#pragma vtordisp(2)
+#pragma vtordisp(push, 1)
+#pragma vtordisp(pop)
+#pragma warning(default : 1; 2 : 2)
+#pragma GCC visibility push(default)
+#pragma GCC visibility pop
+#pragma GCC visibility push(protected)
+#pragma GCC visibility pop
+
+// __pragma captures expanded tokens, so even LexUnexpandedToken can return
+// annotations to the namespace dispatch and pragma handlers.
+__pragma(GCC _Pragma("weak foobar") ivdep)
+__pragma(GCC visibility _Pragma("weak foobar")) // expected-warning {{expected
identifier in '#pragma visibility' - ignored}}
+__pragma(GCC visibility push(_Pragma("weak foobar"))) // expected-warning
{{expected identifier in '#pragma visibility' - ignored}}
+__pragma(clang arc_cf_code_audited _Pragma("weak foobar")) // expected-error
{{expected 'begin' or 'end'}}
+__pragma(clang assume_nonnull _Pragma("weak foobar")) // expected-error
{{expected 'begin' or 'end'}}
+__pragma(clang __set_pp_state _Pragma("weak foobar")) // expected-error
{{expected identifier after '#pragma clang __set_pp_state'}}
+__pragma(clang __debug macro _Pragma("weak foobar")) // expected-warning
{{missing argument to debug command 'macro'}}
+__pragma(clang __debug module_lookup _Pragma("weak foobar")) //
expected-warning {{missing argument to debug command 'module_lookup'}}
+__pragma(clang __debug modules _Pragma("weak foobar")) // expected-warning
{{missing argument to debug command 'modules'}}
+
+// Macro names may be keywords.
+#define int int
+__pragma(clang __debug macro int)
+#undef int
+
+int after_pragmas;
+int use_after_pragmas(void) { return after_pragmas; }
diff --git a/clang/unittests/Lex/LexerTest.cpp
b/clang/unittests/Lex/LexerTest.cpp
index 8f632ccc5b931..6dd78d0d1ade8 100644
--- a/clang/unittests/Lex/LexerTest.cpp
+++ b/clang/unittests/Lex/LexerTest.cpp
@@ -22,6 +22,7 @@
#include "clang/Lex/MacroArgs.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/ModuleLoader.h"
+#include "clang/Lex/Pragma.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "llvm/ADT/ArrayRef.h"
@@ -135,6 +136,30 @@ class LexerTest : public ::testing::Test {
std::string PreDefines;
};
+TEST_F(LexerTest, PragmaKeywordAliasesKeepTheirNames) {
+ class RecordingPragmaHandler : public PragmaHandler {
+ std::vector<std::string> &Names;
+
+ public:
+ RecordingPragmaHandler(StringRef Name, std::vector<std::string> &Names)
+ : PragmaHandler(Name), Names(Names) {}
+
+ void HandlePragma(Preprocessor &, PragmaIntroducer, Token &) override {
+ Names.push_back(getName().str());
+ }
+ };
+
+ std::vector<std::string> Names;
+ for (StringRef Name : {"const", "__const__"})
+ PP->AddPragmaHandler(new RecordingPragmaHandler(Name, Names));
+ PP->AddPragmaHandler("test", new RecordingPragmaHandler("__const__", Names));
+
+ Lex("#pragma const\n"
+ "#pragma __const__\n"
+ "#pragma test __const__\n");
+ EXPECT_THAT(Names, ElementsAre("const", "__const__", "__const__"));
+}
+
TEST_F(LexerTest, GetSourceTextExpandsToMaximumInMacroArgument) {
std::vector<tok::TokenKind> ExpectedTokens;
ExpectedTokens.push_back(tok::identifier);
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits