https://github.com/ChenMiaoi created https://github.com/llvm/llvm-project/pull/230895
Nested pragmas can leave empty quoted arguments in diagnostics or let an inner end-of-directive token truncate the enclosing pragma. Omit annotation argument spellings and track pragma lexer boundaries during argument reads and `__pragma` capture, including captures that finish outside a macro expansion. Depends on #228990. This draft starts from `main` and contains the diagnostic and recovery improvements split from that PR; the crash fixes remain in #228990. After it merges, merge the updated `main`, reconcile the overlapping diagnostic calls, update the crash regression expectations, and complete runtime validation. Validation: C++ syntax checks passed for all four modified implementation files; the Parse diagnostic table generated successfully; `git clang-format` and whitespace checks passed. The new nested-annotation runtime tests await the crash-fix dependency. AI tools assisted with splitting the changes and preparing validation. >From e0ab017a87828d5bc72d519a4c0a3b169f29fb47 Mon Sep 17 00:00:00 2001 From: Chen Miao <[email protected]> Date: Sun, 11 Oct 2026 13:25:15 +0800 Subject: [PATCH] [clang] Improve diagnostics and recovery for nested pragmas --- clang/docs/ReleaseNotes.md | 4 ++ .../clang/Basic/DiagnosticParseKinds.td | 10 ++-- clang/include/clang/Lex/Preprocessor.h | 9 ++++ clang/include/clang/Lex/TokenLexer.h | 7 +++ clang/lib/Lex/Pragma.cpp | 31 +++++++++++ clang/lib/Lex/Preprocessor.cpp | 14 ++++- clang/lib/Lex/TokenLexer.cpp | 2 + clang/lib/Parse/ParsePragma.cpp | 53 ++++++++++++++----- .../Parser/pragma-nested-recovery-target.c | 19 +++++++ clang/test/Parser/pragma-nested-recovery.c | 48 +++++++++++++++++ .../Preprocessor/print-pragma-microsoft.c | 12 +++++ 11 files changed, 191 insertions(+), 18 deletions(-) create mode 100644 clang/test/Parser/pragma-nested-recovery-target.c create mode 100644 clang/test/Parser/pragma-nested-recovery.c diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 77a1e73bc8af6..c7f453b1113a6 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -1179,6 +1179,10 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use #### Diagnostic changes +- Improved diagnostics and error recovery for nested `_Pragma` and Microsoft + `__pragma` expressions, including arguments that cross macro expansion + boundaries. Annotation arguments no longer appear as empty quoted strings, + and inner pragma terminators no longer disrupt subsequent declarations. - For self-assignments during initialization (`T v = v;`), `core.uninitialized.Assign` will not report them as uninitialized accesses (except C++ reference types), and the checks will be delayed until the first accesses of these variables; `deadcode.DeadStores` will not report them as dead stores. (#GH187530) (release-notes-sanitizers)= diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 594d158d25e92..df82177ebe4b5 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1088,8 +1088,10 @@ def warn_pragma_expected_string : Warning< "expected string literal in '#pragma %0' - ignoring">, InGroup<IgnoredPragmas>; def warn_pragma_missing_argument : Warning< "missing argument to '#pragma %0'%select{|; expected %2}1">, InGroup<IgnoredPragmas>; +// Annotation tokens have no source spelling; allow omitting it. def warn_pragma_invalid_argument : Warning< - "unexpected argument '%0' to '#pragma %1'%select{|; expected %3}2">, InGroup<IgnoredPragmas>; + "unexpected argument%select{ '%0'|}4 to '#pragma %1'" + "%select{|; expected %3}2">, InGroup<IgnoredPragmas>; def err_pragma_misplaced_in_decl : Error<"this pragma cannot appear in %0 declaration">; @@ -1217,10 +1219,10 @@ def err_pragma_pointers_to_members_unknown_kind : Error< "'single_inheritance', 'multiple_inheritance', or 'virtual_inheritance'">; // - #pragma clang optimize on/off def err_pragma_optimize_invalid_argument : Error< - "unexpected argument '%0' to '#pragma clang optimize'; " + "unexpected argument%select{ '%0'|}1 to '#pragma clang optimize'; " "expected 'on' or 'off'">; def err_pragma_optimize_extra_argument : Error< - "unexpected extra argument '%0' to '#pragma clang optimize'">; + "unexpected extra argument%select{ '%0'|}1 to '#pragma clang optimize'">; // - #pragma clang attribute def err_pragma_attribute_expected_push_pop_paren : Error< "expected 'push', 'pop', or '(' after '#pragma clang attribute'">; @@ -1597,7 +1599,7 @@ def note_pragma_loop_invalid_vectorize_option : Note< def err_pragma_fp_invalid_option : Error< "%select{invalid|missing}0 option%select{ %1|}0; expected 'contract', 'reassociate', 'reciprocal', or 'exceptions'">; def err_pragma_fp_invalid_argument : Error< - "unexpected argument '%0' to '#pragma clang fp %1'; expected " + "unexpected argument%select{ '%0'|}3 to '#pragma clang fp %1'; expected " "%select{" "'fast' or 'on' or 'off'|" "'on' or 'off'|" diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 31b68a0fd0670..1f1b5253bfb3f 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -883,6 +883,10 @@ class Preprocessor { }; std::vector<IncludeStackInfo> IncludeMacroStack; + /// Stack depths of the pragmas whose arguments are being read. Track all + /// active scopes so that macro stack pops update enclosing boundaries too. + SmallVector<size_t, 4> PragmaLexerStackDepths; + /// Actions invoked when some preprocessor activity is /// encountered (e.g. a file is \#included, etc). std::unique_ptr<PPCallbacks> Callbacks; @@ -2655,6 +2659,11 @@ class Preprocessor { CurLexerSubmodule = IncludeMacroStack.back().TheSubmodule; CurLexerCallback = IncludeMacroStack.back().CurLexerCallback; IncludeMacroStack.pop_back(); + + // A __pragma can start in a macro and finish in its enclosing token stream. + // Keep all active pragma boundaries within the remaining lexer stack. + for (size_t &Depth : PragmaLexerStackDepths) + Depth = std::min(Depth, IncludeMacroStack.size()); } void PropagateLineStartLeadingSpaceInfo(Token &Result); diff --git a/clang/include/clang/Lex/TokenLexer.h b/clang/include/clang/Lex/TokenLexer.h index 0c0c574267364..df7b0bebc4c06 100644 --- a/clang/include/clang/Lex/TokenLexer.h +++ b/clang/include/clang/Lex/TokenLexer.h @@ -104,6 +104,9 @@ class TokenLexer { /// directive. bool LexingCXXModuleDirective : 1; + /// This lexer contains the captured tokens of a __pragma directive. + bool IsPragmaLexer : 1; + public: /// Create a TokenLexer for the specified macro with the specified actual /// arguments. Note that this ctor takes ownership of the ActualArgs pointer. @@ -155,6 +158,10 @@ class TokenLexer { /// preprocessor directive. bool isParsingPreprocessorDirective() const; + /// Return true if this lexer contains the captured tokens of a __pragma + /// directive. + bool isPragmaLexer() const { return IsPragmaLexer; } + /// setLexingCXXModuleDirective - This is set to true if this TokenLexer is /// created when handling a C++ module directive. void setLexingCXXModuleDirective(bool Val = true); diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 078842ab08c5e..f227cc5f99336 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -35,6 +35,7 @@ #include "clang/Lex/TokenLexer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" @@ -170,6 +171,21 @@ void Preprocessor::HandlePragmaDirective(PragmaIntroducer Introducer) { ++NumPragma; + // Record this pragma's lexer stack depth so Lex() can distinguish its eod + // from an eod left by a nested pragma. For example: + // + // #pragma vtordisp(_Pragma("clang __debug dump x")) + // int after; + // + // The inner __debug dump leaves its arguments and eod for the parser: + // + // annot_pragma_dump x <inner eod> ) <outer eod> + // + // Lex() must skip the inner eod so the outer handler's error recovery also + // consumes the trailing ')' and leaves the following declaration intact. + PragmaLexerStackDepths.push_back(IncludeMacroStack.size()); + llvm::scope_exit PragmaScope([this] { PragmaLexerStackDepths.pop_back(); }); + // Invoke the first level of pragma handlers which reads the namespace id. Token Tok; PragmaHandlers->HandlePragma(*this, Introducer, Tok); @@ -353,6 +369,16 @@ void clang::prepare_PragmaString(SmallVectorImpl<char> &StrVal) { /// HandleMicrosoft__pragma - Like Handle_Pragma except the pragma text /// is not enclosed within a string literal. void Preprocessor::HandleMicrosoft__pragma(Token &Tok) { + // Expanding the captured arguments can execute nested pragmas before the + // outer handler runs. For example: + // + // __pragma(optimize("", _Pragma("clang __debug dump x"))) + // + // Keep a boundary active during capture too, so Lex() skips the inner + // __debug dump's leftover eod rather than including it in PragmaToks. + PragmaLexerStackDepths.push_back(IncludeMacroStack.size()); + llvm::scope_exit PragmaScope([this] { PragmaLexerStackDepths.pop_back(); }); + // During macro pre-expansion, check the syntax now but put the tokens back // into the token stream for later consumption. Same as Handle_Pragma. TokenCollector Toks = {*this, InMacroArgPreExpansion, {}, Tok}; @@ -402,6 +428,7 @@ void Preprocessor::HandleMicrosoft__pragma(Token &Tok) { // Push the tokens onto the stack. EnterTokenStream(TokArray, PragmaToks.size(), true, true, /*IsReinject*/ false); + CurTokenLexer->IsPragmaLexer = true; // With everything set up, lex this as a #pragma directive. HandlePragmaDirective({PIK___pragma, PragmaLoc}); @@ -1487,6 +1514,8 @@ struct PragmaWarningHandler : public PragmaHandler { II = Tok.getIdentifierInfo(); if (!II && !Tok.is(tok::numeric_constant)) { PP.Diag(Tok, diag::warn_pragma_warning_spec_invalid); + if (Tok.isAnnotation()) + PP.DiscardUntilEndOfDirective(); return; } @@ -1630,6 +1659,8 @@ struct PragmaExecCharsetHandler : public PragmaHandler { Callbacks->PragmaExecCharsetPop(DiagLoc); } else { PP.Diag(Tok, diag::warn_pragma_exec_charset_spec_invalid); + if (Tok.isAnnotation()) + PP.DiscardUntilEndOfDirective(); return; } diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 84907adc5d744..20626fa87466a 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -981,8 +981,18 @@ void Preprocessor::Lex(Token &Result) { ++LexLevel; // We loop here until a lex function returns a token; this avoids recursion. - while (!CurLexerCallback(*this, Result)) - ; + do { + while (!CurLexerCallback(*this, Result)) + ; + // A nested pragma can defer parsing by injecting an annotation followed by + // its arguments and an eod. Its eod must not terminate the enclosing + // pragma. Only skip eod from actual pragma lexers; a token stream can also + // replay the enclosing pragma's own eod, for example when producing -E + // output. + } while (Result.is(tok::eod) && !PragmaLexerStackDepths.empty() && + IncludeMacroStack.size() > PragmaLexerStackDepths.back() && + ((CurLexer && CurLexer->isPragmaLexer()) || + (CurTokenLexer && CurTokenLexer->isPragmaLexer()))); if (Result.is(tok::unknown) && TheModuleLoader.HadFatalFailure) return; diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp index 1e6132887e319..7478e820a8693 100644 --- a/clang/lib/Lex/TokenLexer.cpp +++ b/clang/lib/Lex/TokenLexer.cpp @@ -58,6 +58,7 @@ void TokenLexer::Init(Token &Tok, SourceLocation ELEnd, MacroInfo *MI, NumTokens = Macro->tokens_end()-Macro->tokens_begin(); MacroExpansionStart = SourceLocation(); LexingCXXModuleDirective = false; + IsPragmaLexer = false; SourceManager &SM = PP.getSourceManager(); MacroStartSLocOffset = SM.getNextLocalOffset(); @@ -115,6 +116,7 @@ void TokenLexer::Init(const Token *TokArray, unsigned NumToks, NextTokGetsSpace = false; MacroExpansionStart = SourceLocation(); LexingCXXModuleDirective = false; + IsPragmaLexer = false; // Set HasLeadingSpace/AtStartOfLine so that the first token will be // returned unmodified. diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 04e7bb5a9b7dd..e1a1b12136837 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -2846,6 +2846,8 @@ void PragmaMSPointersToMembers::HandlePragma(Preprocessor &PP, if (!Arg) { PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) << "pointers_to_members"; + if (Tok.isAnnotation()) + PP.DiscardUntilEndOfDirective(); return; } PP.Lex(Tok); @@ -2863,6 +2865,8 @@ void PragmaMSPointersToMembers::HandlePragma(Preprocessor &PP, PP.Diag(Tok.getLocation(), diag::err_pragma_pointers_to_members_unknown_kind) << Tok.getKind() << /*OnlyInheritanceModels*/ 0; + if (Tok.isAnnotation()) + PP.DiscardUntilEndOfDirective(); return; } PP.Lex(Tok); @@ -2982,6 +2986,8 @@ void PragmaMSVtorDisp::HandlePragma(Preprocessor &PP, } else { PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_action) << "vtordisp"; + if (Tok.isAnnotation()) + PP.DiscardUntilEndOfDirective(); return; } } @@ -3337,8 +3343,11 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP, return; } if (Tok.isNot(tok::identifier)) { + bool IsAnnotation = Tok.isAnnotation(); PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_invalid_argument) - << PP.getSpelling(Tok); + << PP.getSpelling(Tok) << IsAnnotation; + if (IsAnnotation) + PP.DiscardUntilEndOfDirective(); return; } const IdentifierInfo *II = Tok.getIdentifierInfo(); @@ -3348,14 +3357,17 @@ 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) << /*OmitArgument=*/false; return; } PP.Lex(Tok); if (Tok.isNot(tok::eod)) { + bool IsAnnotation = Tok.isAnnotation(); PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_extra_argument) - << PP.getSpelling(Tok); + << PP.getSpelling(Tok) << IsAnnotation; + if (IsAnnotation) + PP.DiscardUntilEndOfDirective(); return; } @@ -3418,9 +3430,12 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, // Don't diagnose if we have an eval_metod pragma with "double" kind. if (Tok.isNot(tok::identifier) && !isEvalMethodDouble) { + bool IsAnnotation = Tok.isAnnotation(); PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) << PP.getSpelling(Tok) << OptionInfo->getName() - << static_cast<int>(*FlagKind); + << static_cast<int>(*FlagKind) << IsAnnotation; + if (IsAnnotation) + PP.DiscardUntilEndOfDirective(); return; } const IdentifierInfo *II = Tok.getIdentifierInfo(); @@ -3435,7 +3450,8 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, .Default(std::nullopt); if (!AnnotValue->ContractValue) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) - << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind; + << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind + << /*OmitArgument=*/false; return; } } else if (FlagKind == PFK_Reassociate || FlagKind == PFK_Reciprocal) { @@ -3448,7 +3464,8 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, .Default(std::nullopt); if (!Value) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) - << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind; + << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind + << /*OmitArgument=*/false; return; } } else if (FlagKind == PFK_Exceptions) { @@ -3461,7 +3478,8 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, .Default(std::nullopt); if (!AnnotValue->ExceptionsValue) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) - << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind; + << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind + << /*OmitArgument=*/false; return; } } else if (FlagKind == PFK_EvalMethod) { @@ -3474,7 +3492,8 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, .Default(std::nullopt); if (!AnnotValue->EvalMethodValue) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) - << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind; + << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind + << /*OmitArgument=*/false; return; } } @@ -3885,9 +3904,10 @@ bool Parser::HandlePragmaMSOptimize(StringRef PragmaName, } IdentifierInfo *II = Tok.getIdentifierInfo(); if (!II || (!II->isStr("on") && !II->isStr("off"))) { + bool IsAnnotation = Tok.isAnnotation(); PP.Diag(PragmaLocation, diag::warn_pragma_invalid_argument) << PP.getSpelling(Tok) << PragmaName << /*Expected=*/true - << "'on' or 'off'"; + << "'on' or 'off'" << IsAnnotation; return false; } bool IsOn = II->isStr("on"); @@ -3901,7 +3921,7 @@ bool Parser::HandlePragmaMSOptimize(StringRef PragmaName, if (!OptimizationList->getString().empty()) { PP.Diag(PragmaLocation, diag::warn_pragma_invalid_argument) << OptimizationList->getString() << PragmaName << /*Expected=*/true - << "\"\""; + << "\"\"" << /*OmitArgument=*/false; return false; } @@ -3972,6 +3992,8 @@ void PragmaForceCUDAHostDeviceHandler::HandlePragma( if (!Info || (!Info->isStr("begin") && !Info->isStr("end"))) { PP.Diag(FirstTok.getLocation(), diag::warn_pragma_force_cuda_host_device_bad_arg); + if (Tok.isAnnotation()) + PP.DiscardUntilEndOfDirective(); return; } @@ -4230,8 +4252,12 @@ void PragmaRISCVHandler::HandlePragma(Preprocessor &PP, IdentifierInfo *II = Tok.getIdentifierInfo(); if (!II || !II->isStr("intrinsic")) { + bool IsAnnotation = Tok.isAnnotation(); PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument) - << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'intrinsic'"; + << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'intrinsic'" + << IsAnnotation; + if (IsAnnotation) + PP.DiscardUntilEndOfDirective(); return; } @@ -4239,9 +4265,12 @@ void PragmaRISCVHandler::HandlePragma(Preprocessor &PP, II = Tok.getIdentifierInfo(); if (!II || !(II->isStr("vector") || II->isStr("sifive_vector") || II->isStr("andes_vector"))) { + bool IsAnnotation = Tok.isAnnotation(); PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument) << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true - << "'vector', 'sifive_vector' or 'andes_vector'"; + << "'vector', 'sifive_vector' or 'andes_vector'" << IsAnnotation; + if (IsAnnotation) + PP.DiscardUntilEndOfDirective(); return; } diff --git a/clang/test/Parser/pragma-nested-recovery-target.c b/clang/test/Parser/pragma-nested-recovery-target.c new file mode 100644 index 0000000000000..72515e42e82e8 --- /dev/null +++ b/clang/test/Parser/pragma-nested-recovery-target.c @@ -0,0 +1,19 @@ +// 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 + +#ifdef TEST_RISCV +#pragma clang riscv _Pragma("weak foobar") intrinsic vector // riscv-warning {{unexpected argument to '#pragma riscv'; expected 'intrinsic'}} +#pragma clang riscv intrinsic _Pragma("weak foobar") vector // riscv-warning {{unexpected argument to '#pragma riscv'; expected 'vector', 'sifive_vector' or 'andes_vector'}} +#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}} +#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-recovery.c b/clang/test/Parser/pragma-nested-recovery.c new file mode 100644 index 0000000000000..a7529e7868a44 --- /dev/null +++ b/clang/test/Parser/pragma-nested-recovery.c @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fsyntax-only -fms-extensions -verify %s +// RUN: %clang_cc1 -fsyntax-only -fms-extensions -fms-compatibility -verify %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -fms-extensions -verify %s + +// Annotation arguments have no spelling. Omit the empty quoted argument and +// consume the remaining directive, so subsequent declarations remain intact. +#pragma clang optimize _Pragma("weak foobar") on // expected-error {{unexpected argument to '#pragma clang optimize'; expected 'on' or 'off'}} +#pragma clang optimize on _Pragma("weak foobar") // expected-error {{unexpected extra argument to '#pragma clang optimize'}} +#pragma clang fp contract(_Pragma("weak foobar")) // expected-error {{unexpected argument to '#pragma clang fp contract'; expected 'fast' or 'on' or 'off'}} +#pragma optimize("", _Pragma("weak foobar")) // expected-warning {{unexpected argument to '#pragma optimize'; expected 'on' or 'off'}} + +#pragma vtordisp(_Pragma("weak foobar")) // expected-warning {{unknown action for '#pragma vtordisp' - ignored}} +#pragma pointers_to_members(_Pragma("weak foobar")) // expected-warning {{expected identifier in '#pragma pointers_to_members' - ignored}} +#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'}} +#pragma warning(_Pragma("weak foobar")) // expected-warning {{#pragma warning expected 'push', 'pop', 'default', 'disable', 'error', 'once', 'suppress', 1, 2, 3, or 4}} +#pragma execution_character_set(_Pragma("weak foobar")) // expected-warning {{#pragma execution_character_set expected 'push' or 'pop'}} + +// Ordinary invalid arguments retain their original spelling. +#pragma clang optimize 42 // expected-error {{unexpected argument '42' to '#pragma clang optimize'; expected 'on' or 'off'}} +#pragma clang optimize on 42 // expected-error {{unexpected extra argument '42' to '#pragma clang optimize'}} +#pragma clang fp contract(42) // expected-error {{unexpected argument '42' to '#pragma clang fp contract'; expected 'fast' or 'on' or 'off'}} +#pragma optimize("", 42) // expected-warning {{unexpected argument '42' to '#pragma optimize'; expected 'on' or 'off'}} + +// __debug dump leaves its arguments and eod for the parser. Its inner eod +// must not terminate the enclosing pragma's recovery or argument capture. +#pragma vtordisp(_Pragma("clang __debug dump x")) // expected-warning {{unknown action for '#pragma vtordisp' - ignored}} +int after_vtordisp; +#pragma optimize("", _Pragma("clang __debug dump x")) // expected-warning {{unexpected argument to '#pragma optimize'; expected 'on' or 'off'}} +int after_ms_optimize; +__pragma(optimize("", _Pragma("clang __debug dump x"))) // expected-warning {{unexpected argument to '#pragma optimize'; expected 'on' or 'off'}} +int after_ms_capture; + +// Captured __pragma token streams have their own eod too. +#pragma vtordisp(__pragma(clang __debug dump x)) // expected-warning {{unknown action for '#pragma vtordisp' - ignored}} +int after_nested_ms_pragma; +__pragma(optimize("", __pragma(clang __debug dump x))) // expected-warning {{unexpected argument to '#pragma optimize'; expected 'on' or 'off'}} +int after_nested_ms_capture; + +// Both captures start in the same macro, then finish after its lexer is popped. +#define BEGIN_NESTED_PRAGMA __pragma(optimize("", __pragma(clang __debug dump +BEGIN_NESTED_PRAGMA x))) // expected-warning {{unexpected argument to '#pragma optimize'; expected 'on' or 'off'}} +#undef BEGIN_NESTED_PRAGMA +int after_macro; + +int use_after_pragmas(void) { + return after_vtordisp + after_ms_optimize + after_ms_capture + + after_nested_ms_pragma + after_nested_ms_capture + after_macro; +} diff --git a/clang/test/Preprocessor/print-pragma-microsoft.c b/clang/test/Preprocessor/print-pragma-microsoft.c index d121d93f0d857..20244db4f7aa4 100644 --- a/clang/test/Preprocessor/print-pragma-microsoft.c +++ b/clang/test/Preprocessor/print-pragma-microsoft.c @@ -18,3 +18,15 @@ #pragma pack(push, _CRT_PACKING) // CHECK: #pragma pack(push, 8) #pragma pack(pop) + +// Printing an unknown pragma replays its first token for macro expansion. In +// the empty case that token is the outer pragma's eod, which must be preserved. +__pragma() +// CHECK: #pragma{{$}} +int after_empty; +// CHECK: int after_empty; + +__pragma(unknown argument) +// CHECK: #pragma unknown argument +int after_unknown; +// CHECK: int after_unknown; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
