https://github.com/ChenMiaoi created 
https://github.com/llvm/llvm-project/pull/230937

When processing Microsoft `__pragma(mark ...)`, the preprocessor uses a 
captured token stream, so no character lexer is available. `HandlePragmaMark()` 
previously assumed that a character lexer was always present and attempted to 
read to the end of the line, causing the following code to crash:

``` cc
__pragma(mark foo)
```

This patch ignores the mark text when no character lexer is available, consumes 
the remaining pragma arguments, and returns, avoiding an assertion failure or a 
null pointer dereference.

Split by #228990 

>From ea4d4fd036aa20989e3b86f48d7701514fb2f25a Mon Sep 17 00:00:00 2001
From: Chen Miao <[email protected]>
Date: Sun, 11 Oct 2026 15:16:20 +0800
Subject: [PATCH] [clang] Handle __pragma(mark) without a character lexer

---
 clang/docs/ReleaseNotes.md              |  3 +++
 clang/lib/Lex/Pragma.cpp                |  7 ++++++-
 clang/test/Lexer/pragma-mark.c          | 14 ++++++++++++++
 clang/unittests/Lex/PPCallbacksTest.cpp | 16 ++++++++++++++++
 4 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 77a1e73bc8af6..bd54f70c32dc8 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 a crash when processing Microsoft `__pragma(mark ...)` expressions
+  without a character lexer.
+
 - 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..6ab33fc80811b 100644
--- a/clang/lib/Lex/Pragma.cpp
+++ b/clang/lib/Lex/Pragma.cpp
@@ -427,7 +427,12 @@ void Preprocessor::HandlePragmaOnce(Token &OnceTok) {
 }
 
 void Preprocessor::HandlePragmaMark(Token &MarkTok) {
-  assert(CurPPLexer && "No current lexer?");
+  // __pragma uses a captured token stream, so there is no lexer from which to
+  // read the uninterpreted text. Ignore the mark and consume its arguments.
+  if (!CurLexer) {
+    DiscardUntilEndOfDirective();
+    return;
+  }
 
   SmallString<64> Buffer;
   CurLexer->ReadToEndOfLine(&Buffer);
diff --git a/clang/test/Lexer/pragma-mark.c b/clang/test/Lexer/pragma-mark.c
index e58b2267ddbce..3f4f6f29fd89a 100644
--- a/clang/test/Lexer/pragma-mark.c
+++ b/clang/test/Lexer/pragma-mark.c
@@ -1,4 +1,6 @@
 // RUN: %clang_cc1 %s -fsyntax-only -verify
+// RUN: %clang_cc1 %s -fsyntax-only -fms-extensions -DTEST_MS_PRAGMA -verify
+// RUN: %clang_cc1 %s -E -fms-extensions -DTEST_MS_PRAGMA -verify | FileCheck 
%s
 // expected-no-diagnostics
 
 // Lexer diagnostics shouldn't be included in #pragma mark.
@@ -10,3 +12,15 @@ X("mark foo ' bar")
 
 int i;
 
+#ifdef TEST_MS_PRAGMA
+// __pragma has already lexed and expanded its arguments. It must consume the
+// captured tokens without requiring a character lexer, including annotations
+// introduced by nested pragmas.
+__pragma(mark)
+__pragma(mark foo) int after_mark;
+__pragma(mark foo _Pragma("weak foobar")) int after_nested_mark;
+int use_after_marks(void) { return after_mark + after_nested_mark; }
+// CHECK: int after_mark;
+// CHECK: int after_nested_mark;
+// CHECK: int use_after_marks(void) { return after_mark + after_nested_mark; }
+#endif
diff --git a/clang/unittests/Lex/PPCallbacksTest.cpp 
b/clang/unittests/Lex/PPCallbacksTest.cpp
index 169734f80df47..8a04fe9f78909 100644
--- a/clang/unittests/Lex/PPCallbacksTest.cpp
+++ b/clang/unittests/Lex/PPCallbacksTest.cpp
@@ -554,6 +554,22 @@ TEST_F(PPCallbacksTest, CollectMarks) {
   ASSERT_EQ(" - trivia", Marks[3].Trivia);
 }
 
+TEST_F(PPCallbacksTest, CollectMarksWithMicrosoftPragmas) {
+  LangOpts.MicrosoftExt = 1;
+  const char *Source =
+      "#pragma mark - before\n"
+      "__pragma(mark ignored)\n"
+      "__pragma(mark _Pragma(\"mark inner's trivia\") ignored)\n"
+      "#pragma mark - after\n";
+
+  auto Marks = PragmaMarkCall(Source);
+
+  ASSERT_EQ(3u, Marks.size());
+  EXPECT_EQ(" - before", Marks[0].Trivia);
+  EXPECT_EQ(" inner's trivia", Marks[1].Trivia);
+  EXPECT_EQ(" - after", Marks[2].Trivia);
+}
+
 TEST_F(PPCallbacksTest, DirectiveExprRanges) {
   const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
   EXPECT_EQ(Results1.size(), 1U);

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to