https://github.com/ChenMiaoi updated https://github.com/llvm/llvm-project/pull/230927
>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 1/4] [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); >From d3f45ee062695b81146025a25785762f25927e4c Mon Sep 17 00:00:00 2001 From: Chen Miao <[email protected]> Date: Sun, 18 Oct 2026 05:16:28 +0800 Subject: [PATCH 2/4] [clang] Preserve operator keyword names in pragma handlers --- clang/lib/Lex/Pragma.cpp | 34 +++++++++----------- clang/test/Parser/pragma-attribute.cpp | 3 -- clang/test/Parser/pragma-nested-annotation.c | 9 +++--- clang/unittests/Lex/LexerTest.cpp | 18 +++++++++-- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index e47bbc7402ef5..6a2e209ff5510 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -101,6 +101,13 @@ void PragmaNamespace::RemovePragmaHandler(PragmaHandler *Handler) { Handlers.erase(I); } +// Names may be C++ operator keywords, which have punctuator token kinds. +static IdentifierInfo *getPragmaIdentifierInfo(const Token &Tok) { + if (Tok.isAnnotation() || Tok.is(tok::raw_identifier)) + return nullptr; + return Tok.getIdentifierInfo(); +} + void PragmaNamespace::HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, Token &Tok) { // Read the 'namespace' that the directive is in, e.g. STDC. Do not macro @@ -108,11 +115,9 @@ 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.is(tok::identifier) || tok::getKeywordSpelling(Tok.getKind()) - ? Tok.getIdentifierInfo()->getName() - : StringRef(), - /*IgnoreNull=*/false); + IdentifierInfo *II = getPragmaIdentifierInfo(Tok); + PragmaHandler *Handler = + FindHandler(II ? II->getName() : StringRef(), /*IgnoreNull=*/false); if (!Handler) { PP.Diag(Tok, diag::warn_pragma_ignored); return; @@ -922,12 +927,12 @@ void Preprocessor::HandlePragmaSetPPState(PragmaIntroducer Introducer, Token &Tok) { // Lex the macro name we want to set. LexUnexpandedToken(Tok); - if (Tok.isNot(tok::identifier) && !tok::getKeywordSpelling(Tok.getKind())) { + IdentifierInfo *MacroName = getPragmaIdentifierInfo(Tok); + if (!MacroName) { Diag(Tok.getLocation(), diag::err_pp_pragma_set_pp_state_expected_name); return; } - IdentifierInfo *MacroName = Tok.getIdentifierInfo(); if (!isPragmaSetPPStateMacro(MacroName)) { Diag(Tok.getLocation(), diag::err_pp_pragma_set_pp_state_invalid_arg) << MacroName; @@ -1150,10 +1155,7 @@ struct PragmaDebugHandler : public PragmaHandler { } else if (II->isStr("macro")) { Token MacroName; PP.LexUnexpandedToken(MacroName); - auto *MacroII = MacroName.is(tok::identifier) || - tok::getKeywordSpelling(MacroName.getKind()) - ? MacroName.getIdentifierInfo() - : nullptr; + auto *MacroII = getPragmaIdentifierInfo(MacroName); if (MacroII) PP.dumpMacroInfo(MacroII); else @@ -1178,10 +1180,7 @@ struct PragmaDebugHandler : public PragmaHandler { } else if (II->isStr("module_lookup")) { Token MName; PP.LexUnexpandedToken(MName); - auto *MNameII = - MName.is(tok::identifier) || tok::getKeywordSpelling(MName.getKind()) - ? MName.getIdentifierInfo() - : nullptr; + auto *MNameII = getPragmaIdentifierInfo(MName); if (!MNameII) { PP.Diag(MName, diag::warn_pragma_debug_missing_argument) << II->getName(); @@ -1226,10 +1225,7 @@ struct PragmaDebugHandler : public PragmaHandler { Token Kind; PP.LexUnexpandedToken(Kind); - auto *DumpII = - Kind.is(tok::identifier) || tok::getKeywordSpelling(Kind.getKind()) - ? Kind.getIdentifierInfo() - : nullptr; + auto *DumpII = getPragmaIdentifierInfo(Kind); if (!DumpII) { PP.Diag(Kind, diag::warn_pragma_debug_missing_argument) << II->getName(); diff --git a/clang/test/Parser/pragma-attribute.cpp b/clang/test/Parser/pragma-attribute.cpp index 78699c06262cd..e2fb747d035b8 100644 --- a/clang/test/Parser/pragma-attribute.cpp +++ b/clang/test/Parser/pragma-attribute.cpp @@ -182,10 +182,7 @@ _Pragma("clang attribute pop"); // 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 diff --git a/clang/test/Parser/pragma-nested-annotation.c b/clang/test/Parser/pragma-nested-annotation.c index aef659ca0475d..7b4a517629295 100644 --- a/clang/test/Parser/pragma-nested-annotation.c +++ b/clang/test/Parser/pragma-nested-annotation.c @@ -1,5 +1,4 @@ // 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 @@ -54,10 +53,7 @@ void loop_width_second_arg(void) { } // 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 @@ -81,5 +77,10 @@ __pragma(clang __debug modules _Pragma("weak foobar")) // expected-warning {{mis __pragma(clang __debug macro int) #undef int +// MS extensions also permit C++ operator names as macro names. +#define and 1 +#pragma clang __debug macro and +#undef and + 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 6dd78d0d1ade8..8d06ccf0222a7 100644 --- a/clang/unittests/Lex/LexerTest.cpp +++ b/clang/unittests/Lex/LexerTest.cpp @@ -137,6 +137,11 @@ class LexerTest : public ::testing::Test { }; TEST_F(LexerTest, PragmaKeywordAliasesKeepTheirNames) { + LangOpts.CPlusPlus = true; + LangOpts.CXXOperatorNames = true; + PP = std::make_unique<Preprocessor>(PPOpts, Diags, LangOpts, SourceMgr, + HeaderInfo, ModLoader); + class RecordingPragmaHandler : public PragmaHandler { std::vector<std::string> &Names; @@ -150,14 +155,21 @@ TEST_F(LexerTest, PragmaKeywordAliasesKeepTheirNames) { }; std::vector<std::string> Names; - for (StringRef Name : {"const", "__const__"}) + for (StringRef Name : {"const", "__const__", "and", "or"}) PP->AddPragmaHandler(new RecordingPragmaHandler(Name, Names)); PP->AddPragmaHandler("test", new RecordingPragmaHandler("__const__", Names)); + PP->AddPragmaHandler("test", new RecordingPragmaHandler("or", Names)); Lex("#pragma const\n" "#pragma __const__\n" - "#pragma test __const__\n"); - EXPECT_THAT(Names, ElementsAre("const", "__const__", "__const__")); + "#pragma test __const__\n" + "#pragma and\n" + "#pragma or\n" + "#pragma test or\n" + "#pragma &&\n" + "#pragma test ||\n"); + EXPECT_THAT( + Names, ElementsAre("const", "__const__", "__const__", "and", "or", "or")); } TEST_F(LexerTest, GetSourceTextExpandsToMaximumInMacroArgument) { >From 7d454b414929d3fe083b79434899bc7d51c947cd Mon Sep 17 00:00:00 2001 From: Chen Miao <[email protected]> Date: Tue, 20 Oct 2026 02:32:51 +0800 Subject: [PATCH 3/4] [clang] Simplify release note for nested pragma crashes --- clang/docs/ReleaseNotes.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 4867f40c51cae..4dc911a9b3c3d 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -947,9 +947,7 @@ 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 crashes when `_Pragma` is used inside another pragma. (#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) >From fed61187157edd076b1e11d80ef8f57dcd89ae85 Mon Sep 17 00:00:00 2001 From: Chen Miao <[email protected]> Date: Tue, 20 Oct 2026 03:27:45 +0800 Subject: [PATCH 4/4] [clang] Replace pragma keyword unit test with regression coverage --- clang/test/Parser/pragma-nested-annotation.c | 41 ++++++++++++++++---- clang/unittests/Lex/LexerTest.cpp | 37 ------------------ 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/clang/test/Parser/pragma-nested-annotation.c b/clang/test/Parser/pragma-nested-annotation.c index 7b4a517629295..78cfa7ede694b 100644 --- a/clang/test/Parser/pragma-nested-annotation.c +++ b/clang/test/Parser/pragma-nested-annotation.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -fms-extensions -verify=expected,c %s -// RUN: %clang_cc1 -x c++ -fsyntax-only -fms-extensions -verify=expected,cxx %s +// RUN: %clang_cc1 -fsyntax-only -fms-extensions -verify=expected,c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -fms-extensions -verify=expected,cxx %s 2>&1 | FileCheck %s // Nested _Pragma annotations must be rejected without querying identifier // information on the annotation token. Existing recovery can leave @@ -72,15 +72,42 @@ __pragma(clang __debug macro _Pragma("weak foobar")) // expected-warning {{missi __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. +// Keywords and aliases must retain their original macro names. +#define const 1 +#pragma clang __debug macro const +// CHECK-LABEL: MacroState {{.*}} const{{$}} +// CHECK: #define <macro> 1{{$}} +#undef const + +#define __const__ 2 +#pragma clang __debug macro __const__ +// CHECK-LABEL: MacroState {{.*}} __const__{{$}} +// CHECK: #define <macro> 2{{$}} +#undef __const__ + +// C++ operator keywords carry identifier information despite punctuator kinds. +#define and 3 +#pragma clang __debug macro and +// CHECK-LABEL: MacroState {{.*}} and{{$}} +// CHECK: #define <macro> 3{{$}} +#undef and + +#define or 4 +#pragma clang __debug macro or +// CHECK-LABEL: MacroState {{.*}} or{{$}} +// CHECK: #define <macro> 4{{$}} +#undef or + +// A self-referential macro preserves the keyword through __pragma expansion. #define int int __pragma(clang __debug macro int) +// CHECK-LABEL: MacroState {{.*}} int{{$}} +// CHECK: #define <macro> int{{$}} #undef int -// MS extensions also permit C++ operator names as macro names. -#define and 1 -#pragma clang __debug macro and -#undef and +// Punctuators must not be treated as operator keyword names. +#pragma clang __debug macro && // expected-warning {{missing argument to debug command 'macro'}} +#pragma clang __debug macro || // expected-warning {{missing argument to debug command 'macro'}} 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 8d06ccf0222a7..8f632ccc5b931 100644 --- a/clang/unittests/Lex/LexerTest.cpp +++ b/clang/unittests/Lex/LexerTest.cpp @@ -22,7 +22,6 @@ #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" @@ -136,42 +135,6 @@ class LexerTest : public ::testing::Test { std::string PreDefines; }; -TEST_F(LexerTest, PragmaKeywordAliasesKeepTheirNames) { - LangOpts.CPlusPlus = true; - LangOpts.CXXOperatorNames = true; - PP = std::make_unique<Preprocessor>(PPOpts, Diags, LangOpts, SourceMgr, - HeaderInfo, ModLoader); - - 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__", "and", "or"}) - PP->AddPragmaHandler(new RecordingPragmaHandler(Name, Names)); - PP->AddPragmaHandler("test", new RecordingPragmaHandler("__const__", Names)); - PP->AddPragmaHandler("test", new RecordingPragmaHandler("or", Names)); - - Lex("#pragma const\n" - "#pragma __const__\n" - "#pragma test __const__\n" - "#pragma and\n" - "#pragma or\n" - "#pragma test or\n" - "#pragma &&\n" - "#pragma test ||\n"); - EXPECT_THAT( - Names, ElementsAre("const", "__const__", "__const__", "and", "or", "or")); -} - 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
