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

Reply via email to