Author: Benjamin Luke
Date: 2026-03-04T15:11:33+01:00
New Revision: 11c11ec2e9fd825f625fd7fcb8318e72fe1b9e4a

URL: 
https://github.com/llvm/llvm-project/commit/11c11ec2e9fd825f625fd7fcb8318e72fe1b9e4a
DIFF: 
https://github.com/llvm/llvm-project/commit/11c11ec2e9fd825f625fd7fcb8318e72fe1b9e4a.diff

LOG: [clang][Lex] Preserve MultipleIncludeOpt state in Lexer::peekNextPPToken 
(#183425)

Fixes https://github.com/llvm/llvm-project/issues/180155.

This is a duplicate of https://github.com/llvm/llvm-project/pull/180700
except that I also added some tests, fine to go with either PR, but we
should add the tests.

peekNextPPToken lexed a token and mutated MIOpt, which could clear the
controlling-macro state for main files in C++20 modules mode.
Save/restore MIOpt in Lexer::peekNextPPToken.

Add regression coverage in
LexerTest.MainFileHeaderGuardedWithCPlusPlusModules that checks to make
sure the controlling macro is properly set in C++20 mode.

Add source level lit test in miopt-peek-restore-header-guard.cpp that
checks to make sure that the warnings that depend on the MIOpt state
machine are emitted in C++20 mode.

Added: 
    clang/test/Preprocessor/miopt-peek-restore-header-guard.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Lex/Lexer.cpp
    clang/unittests/Lex/LexerTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 047cc8455628f..6c26d514865ea 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -308,6 +308,7 @@ Bug Fixes in This Version
 - Fixed an assertion failure in the serialized diagnostic printer when it is 
destroyed without calling ``finish()``. (#GH140433)
 - Fixed an assertion failure caused by error recovery while extending a nested 
name specifier with results from ordinary lookup. (#GH181470)
 - Fixed a crash when parsing ``#pragma clang attribute`` arguments for 
attributes that forbid arguments. (#GH182122)
+- Fixed a bug with multiple-include optimization (MIOpt) state not being 
preserved in some cases during lexing, which could suppress header-guard 
mismatch diagnostics and interfere with include-guard optimization. (#GH180155)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index cbf0c77232db7..13f6bb525c716 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -3231,6 +3231,7 @@ std::optional<Token> Lexer::peekNextPPToken() {
   bool atStartOfLine = IsAtStartOfLine;
   bool atPhysicalStartOfLine = IsAtPhysicalStartOfLine;
   bool leadingSpace = HasLeadingSpace;
+  MultipleIncludeOpt MIOptState = MIOpt;
 
   Token Tok;
   Lex(Tok);
@@ -3241,6 +3242,7 @@ std::optional<Token> Lexer::peekNextPPToken() {
   HasLeadingSpace = leadingSpace;
   IsAtStartOfLine = atStartOfLine;
   IsAtPhysicalStartOfLine = atPhysicalStartOfLine;
+  MIOpt = MIOptState;
   // Restore the lexer back to non-skipping mode.
   LexingRawMode = false;
 

diff  --git a/clang/test/Preprocessor/miopt-peek-restore-header-guard.cpp 
b/clang/test/Preprocessor/miopt-peek-restore-header-guard.cpp
new file mode 100644
index 0000000000000..8f3e9f49cf1b6
--- /dev/null
+++ b/clang/test/Preprocessor/miopt-peek-restore-header-guard.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -Wheader-guard -verify %s
+
+// Regression test for Lexer::peekNextPPToken() saving/restoring MIOpt state.
+// In C++20 module mode, the preprocessor peeks the first pp-token in the main
+// file before lexing begins. That must not perturb MIOpt state used for
+// header-guard analysis.
+
+#ifndef GOOD_GUARD
+#define BAD_GUARD
+#endif
+// expected-warning@-3 {{'GOOD_GUARD' is used as a header guard here, followed 
by #define of a 
diff erent macro}}
+// expected-note@-3 {{'BAD_GUARD' is defined here; did you mean 'GOOD_GUARD'?}}

diff  --git a/clang/unittests/Lex/LexerTest.cpp 
b/clang/unittests/Lex/LexerTest.cpp
index c51cd0d2bfdaa..da335d6e81820 100644
--- a/clang/unittests/Lex/LexerTest.cpp
+++ b/clang/unittests/Lex/LexerTest.cpp
@@ -89,6 +89,30 @@ class LexerTest : public ::testing::Test {
     return toks;
   }
 
+  bool MainFileIsMultipleIncludeGuarded(StringRef Filename, StringRef Source) {
+    FileEntryRef FE = FileMgr.getVirtualFileRef(Filename, Source.size(), 0);
+    SourceMgr.setMainFileID(
+        SourceMgr.createFileID(FE, SourceLocation(), SrcMgr::C_User));
+    SourceMgr.overrideFileContents(
+        FE, llvm::MemoryBuffer::getMemBufferCopy(Source, Filename));
+
+    TrivialModuleLoader ModLoader;
+    HeaderSearchOptions HSOpts;
+    HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get());
+    PreprocessorOptions PPOpts;
+    std::unique_ptr<Preprocessor> LocalPP = std::make_unique<Preprocessor>(
+        PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,
+        /*IILookup=*/nullptr,
+        /*OwnsHeaderSearch=*/false);
+    if (!PreDefines.empty())
+      LocalPP->setPredefines(PreDefines);
+    LocalPP->Initialize(*Target);
+    LocalPP->EnterMainSourceFile();
+    std::vector<Token> Toks;
+    LocalPP->LexTokensUntilEOF(&Toks);
+    return LocalPP->getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE);
+  }
+
   std::string getSourceText(Token Begin, Token End) {
     bool Invalid;
     StringRef Str =
@@ -360,6 +384,16 @@ TEST_F(LexerTest, LexAPI) {
   EXPECT_EQ("N", Lexer::getImmediateMacroName(idLoc4, SourceMgr, LangOpts));
 }
 
+TEST_F(LexerTest, MainFileHeaderGuardedWithCPlusPlusModules) {
+  LangOpts.CPlusPlus = true;
+  LangOpts.CPlusPlusModules = true;
+
+  EXPECT_TRUE(MainFileIsMultipleIncludeGuarded("guarded.h", "#ifndef GUARD\n"
+                                                            "#define GUARD\n"
+                                                            "int x;\n"
+                                                            "#endif\n"));
+}
+
 TEST_F(LexerTest, HandlesSplitTokens) {
   std::vector<tok::TokenKind> ExpectedTokens;
   // Line 1 (after the #defines)


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

Reply via email to