[clang] 11c11ec - [clang][Lex] Preserve MultipleIncludeOpt state in Lexer::peekNextPPToken (#183425)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 4 06:11:40 PST 2026
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 at -3 {{'GOOD_GUARD' is used as a header guard here, followed by #define of a
diff erent macro}}
+// expected-note at -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)
More information about the cfe-commits
mailing list