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
