[clang] [clang] Fix crash in isAtEndOfMacroExpansion at FileID boundary. (PR #191734)

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 15 23:54:11 PDT 2026


https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/191734

>From a68cf3e4a30b93e1fbd764214b07d8b9b9544af4 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Sun, 12 Apr 2026 22:47:58 +0200
Subject: [PATCH 1/2] [clang] Fix crash in isAtEndOfMacroExpansion at FileID
 boundary.

---
 clang/lib/Lex/Lexer.cpp                     | 16 +++++++++--
 clang/test/Parser/macro-braces-recovery.cpp | 31 +++++++++++++++++++++
 2 files changed, 45 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Parser/macro-braces-recovery.cpp

diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 7a3d79e744ef2..012c4b06fb428 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -921,8 +921,20 @@ bool Lexer::isAtEndOfMacroExpansion(SourceLocation loc,
 
   SourceLocation afterLoc = loc.getLocWithOffset(tokLen);
   SourceLocation expansionLoc;
-  if (!SM.isAtEndOfImmediateMacroExpansion(afterLoc, &expansionLoc))
-    return false;
+  FileID FID = SM.getFileID(loc);
+
+  if (SM.isInFileID(afterLoc, FID)) {
+    if (!SM.isAtEndOfImmediateMacroExpansion(afterLoc, &expansionLoc))
+      return false;
+  } else {
+    // During error recovery, a zero-length synthetic token might be inserted
+    // past the end of the FileID, e.g. inserting ")" when a macro-arg
+    // containing a comma should be guarded by parentheses. In this case,
+    // afterLoc reaches the `NextLocalOffset` boundary, any operations on afterLoc will be invalid!
+    const SrcMgr::SLocEntry &Entry = SM.getSLocEntry(FID);
+    assert(Entry.isExpansion() && "Should be in an expansion");
+    expansionLoc = Entry.getExpansion().getExpansionLocEnd();
+  }
 
   if (expansionLoc.isFileID()) {
     // No other macro expansions.
diff --git a/clang/test/Parser/macro-braces-recovery.cpp b/clang/test/Parser/macro-braces-recovery.cpp
new file mode 100644
index 0000000000000..e8842f44bd623
--- /dev/null
+++ b/clang/test/Parser/macro-braces-recovery.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+namespace GH21755 {
+#define M(x) f x // expected-note {{macro 'M' defined here}}
+
+// expected-error at +5 {{too many arguments provided to function-like macro invocation}}
+// expected-note at +4 {{parentheses are required around macro argument containing braced initializer list}}
+// expected-error at +3 {{a type specifier is required for all declarations}}
+// expected-error at +2 {{expected ')'}}
+// expected-note at +1 {{to match this '('}}
+M(0 {,}) // expected-error {{expected ';' after top level declarator}}
+}
+
+namespace GH115007 {
+class Foo { // expected-note {{candidate constructor (the implicit copy constructor) not viable}} \
+            // expected-note {{candidate constructor (the implicit move constructor) not viable}}
+public:
+  Foo(int); // expected-note {{candidate constructor not viable: requires 1 argument, but 2 were provided}}
+  bool operator==(const int l); // expected-note {{candidate function not viable: no known conversion from 'Foo' to 'const int' for 1st argument}}
+};
+#define EQ(x,y) (void)(x == y) // expected-note {{macro 'EQ' defined here}}
+
+void test_EQ() {
+  Foo F = Foo{1};
+  // expected-error at +4 {{too many arguments provided to function-like macro invocation}}
+  // expected-note at +3 {{parentheses are required around macro argument containing braced initializer list}}
+  // expected-error at +2 {{no matching constructor for initialization of 'Foo'}}
+  // expected-error at +1 {{invalid operands to binary expression ('Foo' and 'Foo')}}
+  EQ(F,Foo{1,2});
+}
+}

>From be5158b1cc94558db6578cdd7a30db7e4bf5f094 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 14 Apr 2026 22:14:39 +0200
Subject: [PATCH 2/2] format

---
 clang/docs/ReleaseNotes.rst | 2 +-
 clang/lib/Lex/Lexer.cpp     | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5edec5f04e976..a6b6345168ef2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -500,7 +500,7 @@ Miscellaneous Clang Crashes Fixed
 - Fixed a crash when explicitly casting a scalar to an atomic complex. (#GH114885)
 - Fixed an assertion failure when parsing an invalid out-of-line enum definition with template parameters. (#GH187909)
 - Fixed an assertion failure on invalid template template parameter during typo correction. (#GH183983)
-- Fixed an assertion failure when using CTAD for alias templates where the RHS resolves to a non-dependent class template specialization. (#GH190517)
+- Fixed an assertion failure in ``isAtEndOfMacroExpansion`` on macro expansions crossing the boundary of two fileIDs. (#GH115007), (#GH21755)
 
 OpenACC Specific Changes
 ------------------------
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 012c4b06fb428..e85dc6679d508 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -930,7 +930,8 @@ bool Lexer::isAtEndOfMacroExpansion(SourceLocation loc,
     // During error recovery, a zero-length synthetic token might be inserted
     // past the end of the FileID, e.g. inserting ")" when a macro-arg
     // containing a comma should be guarded by parentheses. In this case,
-    // afterLoc reaches the `NextLocalOffset` boundary, any operations on afterLoc will be invalid!
+    // afterLoc reaches the `NextLocalOffset` boundary, any operations on
+    // afterLoc will be invalid!
     const SrcMgr::SLocEntry &Entry = SM.getSLocEntry(FID);
     assert(Entry.isExpansion() && "Should be in an expansion");
     expansionLoc = Entry.getExpansion().getExpansionLocEnd();



More information about the cfe-commits mailing list