[clang] 59553fa - [clang] avoid reentering header-name lexing on nested macro expansion (#179151)

via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 13 08:58:55 PST 2026


Author: Oleksandr Tarasiuk
Date: 2026-02-13T18:58:50+02:00
New Revision: 59553fa27a52fd95d9f5386a4554e76f3a8e5c9a

URL: https://github.com/llvm/llvm-project/commit/59553fa27a52fd95d9f5386a4554e76f3a8e5c9a
DIFF: https://github.com/llvm/llvm-project/commit/59553fa27a52fd95d9f5386a4554e76f3a8e5c9a.diff

LOG: [clang] avoid reentering header-name lexing on nested macro expansion (#179151)

Fixes #178635

---

This PR addressed the issue when header-name _lexing_ reentered on
nested macro expansion.

`__has_include`/`__has_embed` lex a header name by calling
`LexHeaderName`, and neither expects nested expansion in that context.

If `__has_include` appears inside the header name expression (e.g.,
`__has_include(__has_include)`, or `__has_embed(__has_include)`)


https://github.com/llvm/llvm-project/blob/6a18039298174562a38f28ca06d12dcbf7f14f06/clang/lib/Lex/PPMacroExpansion.cpp#L1252

macro expansion reenters `LexHeaderName` and hits the assertion


https://github.com/llvm/llvm-project/blob/6a18039298174562a38f28ca06d12dcbf7f14f06/clang/lib/Lex/PPMacroExpansion.cpp#L1898


https://github.com/llvm/llvm-project/blob/6a18039298174562a38f28ca06d12dcbf7f14f06/clang/lib/Lex/PPMacroExpansion.cpp#L1156

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Lex/Preprocessor.cpp
    clang/test/Preprocessor/embed___has_embed_parsing_errors.c
    clang/test/Preprocessor/has_include.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 92731326ca1bf..2268622af0a0d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -262,6 +262,7 @@ Bug Fixes in This Version
 - Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
 - Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
 - Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943)
+- Fixed an assertion failure caused by nested macro expansion during header-name lexing (``__has_embed(__has_include)``). (#GH178635)
 
 - Clang now outputs relative paths of embeds for dependency output. (#GH161950)
 - Fixed an assertion failure when evaluating ``_Countof`` on invalid ``void``-typed operands. (#GH180893)

diff  --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 791a9644b6e85..6abcf37079798 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1041,10 +1041,16 @@ void Preprocessor::LexTokensUntilEOF(std::vector<Token> *Tokens) {
 bool Preprocessor::LexHeaderName(Token &FilenameTok, bool AllowMacroExpansion) {
   // Lex using header-name tokenization rules if tokens are being lexed from
   // a file. Just grab a token normally if we're in a macro expansion.
-  if (CurPPLexer)
-    CurPPLexer->LexIncludeFilename(FilenameTok);
-  else
+  if (CurPPLexer) {
+    // Avoid nested header-name lexing when macro expansion recurses
+    // __has_include(__has_include))
+    if (CurPPLexer->ParsingFilename)
+      LexUnexpandedToken(FilenameTok);
+    else
+      CurPPLexer->LexIncludeFilename(FilenameTok);
+  } else {
     Lex(FilenameTok);
+  }
 
   // This could be a <foo/bar.h> file coming from a macro expansion.  In this
   // case, glue the tokens together into an angle_string_literal token.

diff  --git a/clang/test/Preprocessor/embed___has_embed_parsing_errors.c b/clang/test/Preprocessor/embed___has_embed_parsing_errors.c
index 4c6b03069c518..d9f78fa82ecce 100644
--- a/clang/test/Preprocessor/embed___has_embed_parsing_errors.c
+++ b/clang/test/Preprocessor/embed___has_embed_parsing_errors.c
@@ -292,3 +292,14 @@ int a = __has_embed (__FILE__);
    expected-error at +2 {{expected value in expression}}
 #if __has_embed("" limit
 #endif
+
+// expected-error at +2 {{missing '(' after '__has_include'}}
+// expected-error at +1 {{expected "FILENAME" or <FILENAME>}}
+#if __has_embed(__has_include)
+#endif
+
+// expected-error at +3 {{missing '(' after '__has_embed'}}
+// expected-error at +2 {{expected value in expression}}
+// expected-error at +1 {{expected "FILENAME" or <FILENAME>}}
+#if __has_embed(__has_embed)
+#endif

diff  --git a/clang/test/Preprocessor/has_include.c b/clang/test/Preprocessor/has_include.c
index ff199bf23063f..13f9d99b3c080 100644
--- a/clang/test/Preprocessor/has_include.c
+++ b/clang/test/Preprocessor/has_include.c
@@ -148,6 +148,16 @@ MACRO1  // This should be fine because it is never actually reached
 #if __has_include(stdint.h>)
 #endif
 
+// expected-error at +2 {{missing '(' after '__has_include'}}
+// expected-error at +1 {{expected "FILENAME" or <FILENAME>}}
+#if __has_include(__has_include)
+#endif
+
+// expected-error at +2 {{missing '(' after '__has_embed'}}
+// expected-error at +1 {{expected "FILENAME" or <FILENAME>}}
+#if __has_include(__has_embed)
+#endif
+
 // expected-error at +1 {{'__has_include' must be used within a preprocessing directive}}
 __has_include
 


        


More information about the cfe-commits mailing list