[clang] [clang] Fix crash in isAtEndOfMacroExpansion at FileID boundary. (PR #191734)
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Sun Apr 12 13:57:04 PDT 2026
https://github.com/hokein created https://github.com/llvm/llvm-project/pull/191734
During error recovery, a synthetic token (whose length is 0) can be inserted past the end of a FileID, e.g. inserting ")" when a macro-arg containing a comma should be guarded by parentheses.
When calculating the location after this token, the calculated `AfterLoc` can point exactly to the start of the next FileID (`NextLocalOffset`), any source manager operations on the `AfterLoc` is invalid.
This patch adds a safe guard in `Lexer::isAtEndOfMacroExpansion` to prevent passing this invalid location to `SourceManager`.
Fixes #115007
Fixes #21755
>From a3c5022b8de4879073e2fb9c5c6bbcc68be2924e 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] [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 10246552bb13d..989c4e0cb7cef 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -920,8 +920,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});
+}
+}
More information about the cfe-commits
mailing list