https://github.com/yronglin updated 
https://github.com/llvm/llvm-project/pull/225451

>From faf185a05774c273b9b8cd3b4a00f796156f7579 Mon Sep 17 00:00:00 2001
From: yronglin <[email protected]>
Date: Tue, 6 Oct 2026 23:22:46 -0700
Subject: [PATCH] [clang] Fix uses header-name lexing rules when #include is
 followed by an empty macro expansion

Signed-off-by: yronglin <[email protected]>
---
 clang/docs/ReleaseNotes.md                    |  2 ++
 clang/include/clang/Lex/Lexer.h               |  7 +++++
 clang/include/clang/Lex/Preprocessor.h        |  6 +++++
 clang/include/clang/Lex/PreprocessorLexer.h   |  2 +-
 clang/lib/Lex/Lexer.cpp                       | 26 ++++++++++++++++---
 clang/lib/Lex/Preprocessor.cpp                |  9 +++++--
 clang/lib/Lex/PreprocessorLexer.cpp           | 18 -------------
 clang/test/CXX/drs/cwg3015.cpp                | 14 ++++++++++
 clang/test/CXX/drs/cwg30xx.cpp                |  2 ++
 .../ClangScanDeps/depscan-lex-has-include.c   | 11 ++++++++
 clang/test/Preprocessor/_Pragma-dependency.c  |  8 ++++++
 clang/unittests/Lex/LexerTest.cpp             | 21 +++++++++++++++
 .../Lex/PPDependencyDirectivesTest.cpp        | 14 +++++++++-
 clang/www/cxx_dr_status.html                  |  2 +-
 14 files changed, 116 insertions(+), 26 deletions(-)
 create mode 100644 clang/test/CXX/drs/cwg3015.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 08442f74879afc..5578bc4c6db41b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -751,6 +751,8 @@ features cannot lower the translation-unit ABI level;
   by a using-declaration (e.g. `using Base::operator auto;`). Such a conversion
   function is now also diagnosed if it is deleted. (#GH189146)
 
+- Fix incorrect uses header-name lexing rules when ``#include`` is followed by 
an empty macro expansion. (#GH223781)
+
 - A workaround that was introduced to fix an issue with the `<format>` header 
present in some versions of
   libstdc++15 has been extended to support preprocessed input. Previously, 
splitting the preprocessing and
   compilation step would result in the fix not being applied. (#GH160314)
diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h
index b3d8ccdd2d0e30..5037aec7640f0d 100644
--- a/clang/include/clang/Lex/Lexer.h
+++ b/clang/include/clang/Lex/Lexer.h
@@ -202,6 +202,9 @@ class Lexer : public PreprocessorLexer {
   /// return the tok::eof token.  This implicitly involves the preprocessor.
   bool Lex(Token &Result);
 
+  /// Lex a token, producing a header-name token if possible.
+  bool LexIncludeFilename(Token &FilenameTok) override;
+
 private:
   /// Called when the preprocessor is in 'dependency scanning lexing mode'.
   bool LexDependencyDirectiveToken(Token &Result);
@@ -639,6 +642,10 @@ class Lexer : public PreprocessorLexer {
   ///
   bool LexTokenInternal(Token &Result);
 
+  /// Start a new token, applying the pending start-of-line and leading-space
+  /// flags.
+  void InitToken(Token &Result);
+
   bool CheckUnicodeWhitespace(Token &Result, uint32_t C, const char *CurPtr);
 
   bool LexUnicodeIdentifierStart(Token &Result, uint32_t C, const char 
*CurPtr);
diff --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index 31b68a0fd0670e..809fcef77ee9a7 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -3217,6 +3217,12 @@ class Preprocessor {
   static bool CLK_DependencyDirectivesLexer(Preprocessor &P, Token &Result) {
     return P.CurLexer->LexDependencyDirectiveToken(Result);
   }
+  static bool CLK_HeaderNameLexer(Preprocessor &P, Token &Result) {
+    // Header-name rules only apply to the first attempt. If it doesn't produce
+    // a token (e.g. an empty macro was expanded), retry with the normal lexer.
+    P.recomputeCurLexerKind();
+    return P.CurLexer->LexIncludeFilename(Result);
+  }
 };
 
 /// Abstract base class that describes a handler that will receive
diff --git a/clang/include/clang/Lex/PreprocessorLexer.h 
b/clang/include/clang/Lex/PreprocessorLexer.h
index d71fe708ab20a2..f7746331b0adc1 100644
--- a/clang/include/clang/Lex/PreprocessorLexer.h
+++ b/clang/include/clang/Lex/PreprocessorLexer.h
@@ -130,7 +130,7 @@ class PreprocessorLexer {
   // Misc. lexing methods.
 
   /// Lex a token, producing a header-name token if possible.
-  void LexIncludeFilename(Token &FilenameTok);
+  virtual bool LexIncludeFilename(Token &FilenameTok) = 0;
 
   /// Inform the lexer whether or not we are currently lexing a
   /// preprocessor directive.
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 0abc6a56c44383..c8f9fb8754a250 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -3811,9 +3811,7 @@ void Lexer::PropagateLineStartLeadingSpaceInfo(Token 
&Result) {
   // Note that this doesn't affect IsAtPhysicalStartOfLine.
 }
 
-bool Lexer::Lex(Token &Result) {
-  assert(!isDependencyDirectivesLexer());
-
+void Lexer::InitToken(Token &Result) {
   // Start a new token.
   Result.startToken();
 
@@ -3837,6 +3835,12 @@ bool Lexer::Lex(Token &Result) {
     Result.setFlag(Token::LeadingEmptyMacro);
     HasLeadingEmptyMacro = false;
   }
+}
+
+bool Lexer::Lex(Token &Result) {
+  assert(!isDependencyDirectivesLexer());
+
+  InitToken(Result);
 
   bool isRawLex = isLexingRawMode();
   (void) isRawLex;
@@ -4660,6 +4664,22 @@ bool Lexer::LexTokenInternal(Token &Result) {
   goto LexStart;
 }
 
+/// Lex a token, producing a header-name token if the next token starts with
+/// '<' or '"'. Otherwise, lex a normal token.
+bool Lexer::LexIncludeFilename(Token &FilenameTok) {
+  bool returnedToken = false;
+  ParsingFilename = true;
+  if (isDependencyDirectivesLexer())
+    returnedToken = LexDependencyDirectiveToken(FilenameTok);
+  else {
+    InitToken(FilenameTok);
+    returnedToken = LexTokenInternal(FilenameTok);
+  }
+
+  ParsingFilename = false;
+  return returnedToken;
+}
+
 const char *Lexer::convertDependencyDirectiveToken(
     const dependency_directives_scan::Token &DDTok, Token &Result) {
   const char *TokPtr = BufferStart + DDTok.Offset;
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 84907adc5d744a..0298f30d85e2ef 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1125,8 +1125,13 @@ bool Preprocessor::LexHeaderName(Token &FilenameTok, 
bool AllowMacroExpansion) {
     // __has_include(__has_include))
     if (CurPPLexer->ParsingFilename)
       LexUnexpandedToken(FilenameTok);
-    else
-      CurPPLexer->LexIncludeFilename(FilenameTok);
+    else {
+      CurLexerCallback = CLK_HeaderNameLexer;
+      if (AllowMacroExpansion)
+        Lex(FilenameTok);
+      else
+        LexUnexpandedToken(FilenameTok);
+    }
   } else {
     Lex(FilenameTok);
   }
diff --git a/clang/lib/Lex/PreprocessorLexer.cpp 
b/clang/lib/Lex/PreprocessorLexer.cpp
index 3f483c56f8b126..dc4408d0e8df55 100644
--- a/clang/lib/Lex/PreprocessorLexer.cpp
+++ b/clang/lib/Lex/PreprocessorLexer.cpp
@@ -26,24 +26,6 @@ PreprocessorLexer::PreprocessorLexer(Preprocessor *pp, 
FileID fid)
     InitialNumSLocEntries = pp->getSourceManager().local_sloc_entry_size();
 }
 
-/// After the preprocessor has parsed a \#include, lex and
-/// (potentially) macro expand the filename.
-void PreprocessorLexer::LexIncludeFilename(Token &FilenameTok) {
-  assert(ParsingFilename == false && "reentered LexIncludeFilename");
-
-  // We are now parsing a filename!
-  ParsingFilename = true;
-
-  // Lex the filename.
-  if (LexingRawMode)
-    IndirectLex(FilenameTok);
-  else
-    PP->Lex(FilenameTok);
-
-  // We should have obtained the filename now.
-  ParsingFilename = false;
-}
-
 /// getFileEntry - Return the FileEntry corresponding to this FileID.  Like
 /// getFileID(), this only works for lexers with attached preprocessors.
 OptionalFileEntryRef PreprocessorLexer::getFileEntry() const {
diff --git a/clang/test/CXX/drs/cwg3015.cpp b/clang/test/CXX/drs/cwg3015.cpp
new file mode 100644
index 00000000000000..54cdd42e123689
--- /dev/null
+++ b/clang/test/CXX/drs/cwg3015.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -std=c++98 -fexceptions -fcxx-exceptions -pedantic-errors 
-verify-directives -verify %s
+// RUN: %clang_cc1 -std=c++11 -fexceptions -fcxx-exceptions -pedantic-errors 
-verify-directives -verify %s
+// RUN: %clang_cc1 -std=c++14 -fexceptions -fcxx-exceptions -pedantic-errors 
-verify-directives -verify %s
+// RUN: %clang_cc1 -std=c++17 -fexceptions -fcxx-exceptions -pedantic-errors 
-verify-directives -verify %s
+// RUN: %clang_cc1 -std=c++20 -fexceptions -fcxx-exceptions -pedantic-errors 
-verify-directives -verify %s
+// RUN: %clang_cc1 -std=c++23 -fexceptions -fcxx-exceptions -pedantic-errors 
-verify-directives -verify %s
+// RUN: %clang_cc1 -std=c++2c -fexceptions -fcxx-exceptions -pedantic-errors 
-verify-directives -verify %s
+
+// cwg3015: partial
+
+// An intervening empty macro prevents header-name tokenization.
+#define EMPTY
+#include EMPTY "name with \"quotes\".h"
+// expected-error@-1 {{'name with \"quotes\".h' file not found}}
diff --git a/clang/test/CXX/drs/cwg30xx.cpp b/clang/test/CXX/drs/cwg30xx.cpp
index a664fe6b3f073e..1fbc96089d4ce0 100644
--- a/clang/test/CXX/drs/cwg30xx.cpp
+++ b/clang/test/CXX/drs/cwg30xx.cpp
@@ -22,6 +22,8 @@ void f(
 
 } // namespace cwg3005
 
+// cwg3015 is in cwg3015.cpp
+
 namespace cwg3035 { // cwg3035: no
 #if __cplusplus >= 201103L
 static union {
diff --git a/clang/test/ClangScanDeps/depscan-lex-has-include.c 
b/clang/test/ClangScanDeps/depscan-lex-has-include.c
index 4d46e8d7d19e26..bd1c1392b5cf19 100644
--- a/clang/test/ClangScanDeps/depscan-lex-has-include.c
+++ b/clang/test/ClangScanDeps/depscan-lex-has-include.c
@@ -38,6 +38,17 @@
 #include <something/something.h>
 #endif
 
+#undef something
+#define EMPTY
+#define HEADER something/something.h
+
+// An empty macro prevents header-name tokenization, allowing HEADER to expand.
+#if __has_include(EMPTY <HEADER>)
+#include EMPTY <HEADER>
+#else
+#error header not found after empty macro
+#endif
+
 //--- something/something.h
 
 //--- error.c
diff --git a/clang/test/Preprocessor/_Pragma-dependency.c 
b/clang/test/Preprocessor/_Pragma-dependency.c
index 0b785ac87daaf4..7423c9e1714e98 100644
--- a/clang/test/Preprocessor/_Pragma-dependency.c
+++ b/clang/test/Preprocessor/_Pragma-dependency.c
@@ -6,6 +6,14 @@
 // expected-error@+1 {{expected "FILENAME" or <FILENAME>}}
 #pragma GCC dependency self
 
+#define EMPTY
+// FIXME: The diagnostic location is not correct, it's current in
+//    #pragma GCC dependency EMPTY "./_Pragma-dependency.c"
+//                                 ^ expected "FILENAME" or <FILENAME>
+// The real location should be the start of EMPTY.
+// expected-error@+1 {{expected "FILENAME" or <FILENAME>}}
+#pragma GCC dependency EMPTY "./_Pragma-dependency.c"
+
 #define DO_PRAGMA _Pragma 
 #define STR "GCC dependency \"parse.y\"")
 // expected-error@+1 {{'parse.y' file not found}}
diff --git a/clang/unittests/Lex/LexerTest.cpp 
b/clang/unittests/Lex/LexerTest.cpp
index 8f632ccc5b9315..adc4420ef4fdf8 100644
--- a/clang/unittests/Lex/LexerTest.cpp
+++ b/clang/unittests/Lex/LexerTest.cpp
@@ -719,6 +719,27 @@ TEST_F(LexerTest, CreatedFIDCountForPredefinedBuffer) {
             1U);
 }
 
+TEST_F(LexerTest, LexIncludeFilenameResetsToken) {
+  for (const char *Source :
+       {"\"foo\\\n.h\" \"bar.h\"", "<foo\\\n.h> <bar.h>"}) {
+    auto Buffer = llvm::MemoryBuffer::getMemBuffer(Source);
+    FileID FID = SourceMgr.createFileID(std::move(Buffer));
+    Lexer L(FID, SourceMgr.getBufferOrFake(FID), SourceMgr, LangOpts);
+
+    Token Tok;
+    Tok.startToken();
+    ASSERT_TRUE(L.LexIncludeFilename(Tok));
+    ASSERT_TRUE(Tok.is(tok::header_name));
+    EXPECT_TRUE(Tok.needsCleaning());
+
+    ASSERT_TRUE(L.LexIncludeFilename(Tok));
+    ASSERT_TRUE(Tok.is(tok::header_name));
+    EXPECT_FALSE(Tok.needsCleaning());
+    EXPECT_EQ(Source[0] == '"' ? "\"bar.h\"" : "<bar.h>",
+              Lexer::getSpelling(Tok, SourceMgr, LangOpts));
+  }
+}
+
 TEST_F(LexerTest, RawAndNormalLexSameForLineComments) {
   const llvm::StringLiteral Source = R"cpp(
   // First line comment.
diff --git a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp 
b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
index 7b11fc163f6eb8..0e3b26ff8711c6 100644
--- a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
+++ b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
@@ -191,6 +191,16 @@ TEST_F(PPDependencyDirectivesTest, 
HeaderNamesStartingWithCompoundTokens) {
                      "#if __has_include(<<>)\n"
                      "#include <<>\n"
                      "#endif\n"
+                     "#define HEADER \"=\"\n"
+                     "#define HAS_INCLUDE(x) __has_include(x)\n"
+                     "#if HAS_INCLUDE(HEADER)\n"
+                     "#include HEADER\n"
+                     "#endif\n"
+                     "#define EMPTY\n"
+                     "#define NAME =\n"
+                     "#if __has_include(EMPTY <NAME>)\n"
+                     "#include EMPTY <NAME>\n"
+                     "#endif\n"
                      "#include \\\n<=>";
 
   SmallVector<dependency_directives_scan::Token> Tokens;
@@ -246,12 +256,14 @@ TEST_F(PPDependencyDirectivesTest, 
HeaderNamesStartingWithCompoundTokens) {
   PP.EnterMainSourceFile();
   PP.LexTokensUntilEOF();
 
+  EXPECT_FALSE(Diags.hasErrorOccurred());
   SmallVector<std::string> IncludedFilesSlash;
   for (StringRef IncludedFile : IncludedFiles)
     IncludedFilesSlash.push_back(
         llvm::sys::path::convert_to_slash(IncludedFile));
   SmallVector<std::string> ExpectedIncludes{
-      "/source/main.c", "/source/=", "/source/<", "/source/="};
+      "/source/main.c", "/source/=", "/source/<",
+      "/source/=",      "/source/=", "/source/="};
   EXPECT_EQ(IncludedFilesSlash, ExpectedIncludes);
 }
 
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index e7679da30d5c29..9c42ac51bb6f62 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -20922,7 +20922,7 @@ <h2 id="cxxdr">C++ defect report implementation 
status</h2>
     <td>[<a href="https://wg21.link/cpp.include";>cpp.include</a>]</td>
     <td>CD7</td>
     <td>Handling of <I>header-name</I>s for <TT>#include</TT> and 
<TT>#embed</TT></td>
-    <td class="unknown" align="center">Unknown</td>
+    <td class="partial" align="center">Clang 24</td>
   </tr>
   <tr id="3016">
     <td><a 
href="https://cplusplus.github.io/CWG/issues/3016.html";>3016</a></td>

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

Reply via email to