https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/153641
>From 62ab3571fd1c528bab72193deaf0171028d4bb39 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Fri, 15 Aug 2025 02:12:23 +0800 Subject: [PATCH 1/7] [clang] Allow no trivial before C++ module directive Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/include/clang/Lex/Lexer.h | 3 - clang/include/clang/Lex/Preprocessor.h | 11 + clang/include/clang/Lex/Token.h | 15 +- .../clang/Lex/TrivialDirectiveTracer.h | 388 ++++++++++++++++++ clang/include/clang/Sema/Sema.h | 2 +- clang/lib/Lex/Lexer.cpp | 9 - clang/lib/Lex/Preprocessor.cpp | 46 ++- clang/lib/Parse/Parser.cpp | 8 +- clang/lib/Sema/SemaModule.cpp | 6 +- clang/test/CXX/module/cpp.pre/module_decl.cpp | 141 ++++++- clang/unittests/Lex/LexerTest.cpp | 4 +- 11 files changed, 601 insertions(+), 32 deletions(-) create mode 100644 clang/include/clang/Lex/TrivialDirectiveTracer.h diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 06971ff87ab96..423f2ffe2f852 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -143,9 +143,6 @@ class Lexer : public PreprocessorLexer { /// True if this is the first time we're lexing the input file. bool IsFirstTimeLexingFile; - /// True if current lexing token is the first pp-token. - bool IsFirstPPToken; - // NewLinePtr - A pointer to new line character '\n' being lexed. For '\r\n', // it also points to '\n.' const char *NewLinePtr; diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 71b0f8eab3bfa..d51faad255224 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -82,6 +82,7 @@ class PreprocessorLexer; class PreprocessorOptions; class ScratchBuffer; class TargetInfo; +class TrivialDirectiveTracer; namespace Builtin { class Context; @@ -353,6 +354,11 @@ class Preprocessor { /// First pp-token source location in current translation unit. SourceLocation FirstPPTokenLoc; + /// A preprocessor directive tracer to trace whether the preprocessing + /// state changed. These changes would mean most semantically observable + /// preprocessor state, particularly anything that is order dependent. + TrivialDirectiveTracer *DirTracer = nullptr; + /// A position within a C++20 import-seq. class StdCXXImportSeq { public: @@ -609,6 +615,8 @@ class Preprocessor { return State == NamedModuleImplementation && !getName().contains(':'); } + bool isNotAModuleDecl() const { return State == NotAModuleDecl; } + StringRef getName() const { assert(isNamedModule() && "Can't get name from a non named module"); return Name; @@ -3091,6 +3099,9 @@ class Preprocessor { bool setDeserializedSafeBufferOptOutMap( const SmallVectorImpl<SourceLocation> &SrcLocSeqs); + /// Whether allow C++ module directive. + bool hasSeenNoTrivialPPDirective() const; + private: /// Helper functions to forward lexing to the actual lexer. They all share the /// same signature. diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h index fc43e72593b94..c493571e00038 100644 --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -86,12 +86,10 @@ class Token { // macro stringizing or charizing operator. CommaAfterElided = 0x200, // The comma following this token was elided (MS). IsEditorPlaceholder = 0x400, // This identifier is a placeholder. - - IsReinjected = 0x800, // A phase 4 token that was produced before and - // re-added, e.g. via EnterTokenStream. Annotation - // tokens are *not* reinjected. - FirstPPToken = 0x1000, // This token is the first pp token in the - // translation unit. + IsReinjected = 0x800, // A phase 4 token that was produced before and + // re-added, e.g. via EnterTokenStream. Annotation + // tokens are *not* reinjected. + SeenNoTrivialPPDirective = 0x1000, }; tok::TokenKind getKind() const { return Kind; } @@ -321,8 +319,9 @@ class Token { /// lexer uses identifier tokens to represent placeholders. bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); } - /// Returns true if this token is the first pp-token. - bool isFirstPPToken() const { return getFlag(FirstPPToken); } + bool hasSeenNoTrivialPPDirective() const { + return getFlag(SeenNoTrivialPPDirective); + } }; /// Information about the conditional stack (\#if directives) diff --git a/clang/include/clang/Lex/TrivialDirectiveTracer.h b/clang/include/clang/Lex/TrivialDirectiveTracer.h new file mode 100644 index 0000000000000..9d4e0fdc96daf --- /dev/null +++ b/clang/include/clang/Lex/TrivialDirectiveTracer.h @@ -0,0 +1,388 @@ +//===--- TrivialDirectiveTracer.h -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the TrivialDirectiveTracer interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LEX_TRIVIAL_DIRECTIVE_TRACER_H +#define LLVM_CLANG_LEX_TRIVIAL_DIRECTIVE_TRACER_H + +#include "clang/Lex/PPCallbacks.h" + +namespace clang { +class Preprocessor; + +class TrivialDirectiveTracer : public PPCallbacks { + Preprocessor &PP; + bool InMainFile = true; + bool SeenNoTrivialPPDirective = false; + + void setSeenNoTrivialPPDirective(bool Val); + +public: + TrivialDirectiveTracer(Preprocessor &P) : PP(P) {} + + bool hasSeenNoTrivialPPDirective() const; + + /// Callback invoked whenever a source file is entered or exited. + /// + /// \param Loc Indicates the new location. + /// \param PrevFID the file that was exited if \p Reason is ExitFile or the + /// the file before the new one entered for \p Reason EnterFile. + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID = FileID()) override; + + /// Callback invoked whenever the \p Lexer moves to a different file for + /// lexing. Unlike \p FileChanged line number directives and other related + /// pragmas do not trigger callbacks to \p LexedFileChanged. + /// + /// \param FID The \p FileID that the \p Lexer moved to. + /// + /// \param Reason Whether the \p Lexer entered a new file or exited one. + /// + /// \param FileType The \p CharacteristicKind of the file the \p Lexer moved + /// to. + /// + /// \param PrevFID The \p FileID the \p Lexer was using before the change. + /// + /// \param Loc The location where the \p Lexer entered a new file from or the + /// location that the \p Lexer moved into after exiting a file. + void LexedFileChanged(FileID FID, LexedFileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, FileID PrevFID, + SourceLocation Loc) override; + + /// Callback invoked whenever an embed directive has been processed, + /// regardless of whether the embed will actually find a file. + /// + /// \param HashLoc The location of the '#' that starts the embed directive. + /// + /// \param FileName The name of the file being included, as written in the + /// source code. + /// + /// \param IsAngled Whether the file name was enclosed in angle brackets; + /// otherwise, it was enclosed in quotes. + /// + /// \param File The actual file that may be included by this embed directive. + /// + /// \param Params The parameters used by the directive. + void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled, + OptionalFileEntryRef File, + const LexEmbedParametersResult &Params) override { + setSeenNoTrivialPPDirective(true); + } + + /// Callback invoked whenever an inclusion directive of + /// any kind (\c \#include, \c \#import, etc.) has been processed, regardless + /// of whether the inclusion will actually result in an inclusion. + /// + /// \param HashLoc The location of the '#' that starts the inclusion + /// directive. + /// + /// \param IncludeTok The token that indicates the kind of inclusion + /// directive, e.g., 'include' or 'import'. + /// + /// \param FileName The name of the file being included, as written in the + /// source code. + /// + /// \param IsAngled Whether the file name was enclosed in angle brackets; + /// otherwise, it was enclosed in quotes. + /// + /// \param FilenameRange The character range of the quotes or angle brackets + /// for the written file name. + /// + /// \param File The actual file that may be included by this inclusion + /// directive. + /// + /// \param SearchPath Contains the search path which was used to find the file + /// in the file system. If the file was found via an absolute include path, + /// SearchPath will be empty. For framework includes, the SearchPath and + /// RelativePath will be split up. For example, if an include of "Some/Some.h" + /// is found via the framework path + /// "path/to/Frameworks/Some.framework/Headers/Some.h", SearchPath will be + /// "path/to/Frameworks/Some.framework/Headers" and RelativePath will be + /// "Some.h". + /// + /// \param RelativePath The path relative to SearchPath, at which the include + /// file was found. This is equal to FileName except for framework includes. + /// + /// \param SuggestedModule The module suggested for this header, if any. + /// + /// \param ModuleImported Whether this include was translated into import of + /// \p SuggestedModule. + /// + /// \param FileType The characteristic kind, indicates whether a file or + /// directory holds normal user code, system code, or system code which is + /// implicitly 'extern "C"' in C++ mode. + /// + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, + OptionalFileEntryRef File, StringRef SearchPath, + StringRef RelativePath, const Module *SuggestedModule, + bool ModuleImported, + SrcMgr::CharacteristicKind FileType) override { + setSeenNoTrivialPPDirective(true); + } + + /// Callback invoked whenever there was an explicit module-import + /// syntax. + /// + /// \param ImportLoc The location of import directive token. + /// + /// \param Path The identifiers (and their locations) of the module + /// "path", e.g., "std.vector" would be split into "std" and "vector". + /// + /// \param Imported The imported module; can be null if importing failed. + /// + void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path, + const Module *Imported) override { + setSeenNoTrivialPPDirective(true); + } + + /// Callback invoked when the end of the main file is reached. + /// + /// No subsequent callbacks will be made. + void EndOfMainFile() override { setSeenNoTrivialPPDirective(true); } + + /// Callback invoked when a \#ident or \#sccs directive is read. + /// \param Loc The location of the directive. + /// \param str The text of the directive. + /// + void Ident(SourceLocation Loc, StringRef str) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when start reading any pragma directive. + void PragmaDirective(SourceLocation Loc, + PragmaIntroducerKind Introducer) override {} + + /// Callback invoked when a \#pragma comment directive is read. + void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, + StringRef Str) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma mark comment is read. + void PragmaMark(SourceLocation Loc, StringRef Trivia) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma detect_mismatch directive is + /// read. + void PragmaDetectMismatch(SourceLocation Loc, StringRef Name, + StringRef Value) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma clang __debug directive is read. + /// \param Loc The location of the debug directive. + /// \param DebugType The identifier following __debug. + void PragmaDebug(SourceLocation Loc, StringRef DebugType) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma message directive is read. + /// \param Loc The location of the message directive. + /// \param Namespace The namespace of the message directive. + /// \param Kind The type of the message directive. + /// \param Str The text of the message directive. + void PragmaMessage(SourceLocation Loc, StringRef Namespace, + PragmaMessageKind Kind, StringRef Str) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma gcc diagnostic push directive + /// is read. + void PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma gcc diagnostic pop directive + /// is read. + void PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma gcc diagnostic directive is read. + void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, + diag::Severity mapping, StringRef Str) override { + setSeenNoTrivialPPDirective(false); + } + + /// Called when an OpenCL extension is either disabled or + /// enabled with a pragma. + void PragmaOpenCLExtension(SourceLocation NameLoc, const IdentifierInfo *Name, + SourceLocation StateLoc, unsigned State) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma warning directive is read. + void PragmaWarning(SourceLocation Loc, PragmaWarningSpecifier WarningSpec, + ArrayRef<int> Ids) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma warning(push) directive is read. + void PragmaWarningPush(SourceLocation Loc, int Level) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma warning(pop) directive is read. + void PragmaWarningPop(SourceLocation Loc) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma execution_character_set(push) directive + /// is read. + void PragmaExecCharsetPush(SourceLocation Loc, StringRef Str) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma execution_character_set(pop) directive + /// is read. + void PragmaExecCharsetPop(SourceLocation Loc) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma clang assume_nonnull begin directive + /// is read. + void PragmaAssumeNonNullBegin(SourceLocation Loc) override { + setSeenNoTrivialPPDirective(false); + } + + /// Callback invoked when a \#pragma clang assume_nonnull end directive + /// is read. + void PragmaAssumeNonNullEnd(SourceLocation Loc) override { + setSeenNoTrivialPPDirective(false); + } + + /// Called by Preprocessor::HandleMacroExpandedIdentifier when a + /// macro invocation is found. + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range, const MacroArgs *Args) override; + + /// Hook called whenever a macro definition is seen. + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever a macro \#undef is seen. + /// \param MacroNameTok The active Token + /// \param MD A MacroDefinition for the named macro. + /// \param Undef New MacroDirective if the macro was defined, null otherwise. + /// + /// MD is released immediately following this callback. + void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, + const MacroDirective *Undef) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever the 'defined' operator is seen. + /// \param MD The MacroDirective if the name was a macro, null otherwise. + void Defined(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever an \#if is seen. + /// \param Loc the source location of the directive. + /// \param ConditionRange The SourceRange of the expression being tested. + /// \param ConditionValue The evaluated value of the condition. + /// + // FIXME: better to pass in a list (or tree!) of Tokens. + void If(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever an \#elif is seen. + /// \param Loc the source location of the directive. + /// \param ConditionRange The SourceRange of the expression being tested. + /// \param ConditionValue The evaluated value of the condition. + /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. + // FIXME: better to pass in a list (or tree!) of Tokens. + void Elif(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue, SourceLocation IfLoc) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever an \#ifdef is seen. + /// \param Loc the source location of the directive. + /// \param MacroNameTok Information on the token being tested. + /// \param MD The MacroDefinition if the name was a macro, null otherwise. + void Ifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever an \#elifdef branch is taken. + /// \param Loc the source location of the directive. + /// \param MacroNameTok Information on the token being tested. + /// \param MD The MacroDefinition if the name was a macro, null otherwise. + void Elifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + setSeenNoTrivialPPDirective(true); + } + /// Hook called whenever an \#elifdef is skipped. + /// \param Loc the source location of the directive. + /// \param ConditionRange The SourceRange of the expression being tested. + /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. + // FIXME: better to pass in a list (or tree!) of Tokens. + void Elifdef(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever an \#ifndef is seen. + /// \param Loc the source location of the directive. + /// \param MacroNameTok Information on the token being tested. + /// \param MD The MacroDefiniton if the name was a macro, null otherwise. + void Ifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever an \#elifndef branch is taken. + /// \param Loc the source location of the directive. + /// \param MacroNameTok Information on the token being tested. + /// \param MD The MacroDefinition if the name was a macro, null otherwise. + void Elifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + setSeenNoTrivialPPDirective(true); + } + /// Hook called whenever an \#elifndef is skipped. + /// \param Loc the source location of the directive. + /// \param ConditionRange The SourceRange of the expression being tested. + /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. + // FIXME: better to pass in a list (or tree!) of Tokens. + void Elifndef(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever an \#else is seen. + /// \param Loc the source location of the directive. + /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. + void Else(SourceLocation Loc, SourceLocation IfLoc) override { + setSeenNoTrivialPPDirective(true); + } + + /// Hook called whenever an \#endif is seen. + /// \param Loc the source location of the directive. + /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. + void Endif(SourceLocation Loc, SourceLocation IfLoc) override { + setSeenNoTrivialPPDirective(true); + } +}; + +} // namespace clang + +#endif // LLVM_CLANG_LEX_TRIVIAL_DIRECTIVE_TRACER_H diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1dfc276147fd4..b15c9615bebc3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9836,7 +9836,7 @@ class Sema final : public SemaBase { SourceLocation ModuleLoc, ModuleDeclKind MDK, ModuleIdPath Path, ModuleIdPath Partition, ModuleImportState &ImportState, - bool IntroducerIsFirstPPToken); + bool SeenNoTrivialPPDirective); /// The parser has processed a global-module-fragment declaration that begins /// the definition of the global module fragment of the current module unit. diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 1f695b4a8676c..b282a600c0e56 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -174,8 +174,6 @@ void Lexer::InitLexer(const char *BufStart, const char *BufPtr, ExtendedTokenMode = 0; NewLinePtr = nullptr; - - IsFirstPPToken = true; } /// Lexer constructor - Create a new lexer object for the specified buffer @@ -3225,7 +3223,6 @@ std::optional<Token> Lexer::peekNextPPToken() { bool atStartOfLine = IsAtStartOfLine; bool atPhysicalStartOfLine = IsAtPhysicalStartOfLine; bool leadingSpace = HasLeadingSpace; - bool isFirstPPToken = IsFirstPPToken; Token Tok; Lex(Tok); @@ -3236,7 +3233,6 @@ std::optional<Token> Lexer::peekNextPPToken() { HasLeadingSpace = leadingSpace; IsAtStartOfLine = atStartOfLine; IsAtPhysicalStartOfLine = atPhysicalStartOfLine; - IsFirstPPToken = isFirstPPToken; // Restore the lexer back to non-skipping mode. LexingRawMode = false; @@ -3726,11 +3722,6 @@ bool Lexer::Lex(Token &Result) { HasLeadingEmptyMacro = false; } - if (IsFirstPPToken) { - Result.setFlag(Token::FirstPPToken); - IsFirstPPToken = false; - } - bool atPhysicalStartOfLine = IsAtPhysicalStartOfLine; IsAtPhysicalStartOfLine = false; bool isRawLex = isLexingRawMode(); diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index e278846f6f36d..dd5ab0e19ecd4 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -50,6 +50,7 @@ #include "clang/Lex/ScratchBuffer.h" #include "clang/Lex/Token.h" #include "clang/Lex/TokenLexer.h" +#include "clang/Lex/TrivialDirectiveTracer.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -247,8 +248,6 @@ void Preprocessor::DumpToken(const Token &Tok, bool DumpFlags) const { llvm::errs() << " [LeadingSpace]"; if (Tok.isExpandDisabled()) llvm::errs() << " [ExpandDisabled]"; - if (Tok.isFirstPPToken()) - llvm::errs() << " [First pp-token]"; if (Tok.needsCleaning()) { const char *Start = SourceMgr.getCharacterData(Tok.getLocation()); llvm::errs() << " [UnClean='" << StringRef(Start, Tok.getLength()) @@ -577,8 +576,11 @@ void Preprocessor::EnterMainSourceFile() { // export module M; // error: module declaration must occur // // at the start of the translation unit. if (getLangOpts().CPlusPlusModules) { + auto Tracer = std::make_unique<TrivialDirectiveTracer>(*this); + DirTracer = Tracer.get(); + addPPCallbacks(std::move(Tracer)); std::optional<Token> FirstPPTok = CurLexer->peekNextPPToken(); - if (FirstPPTok && FirstPPTok->isFirstPPToken()) + if (FirstPPTok) FirstPPTokenLoc = FirstPPTok->getLocation(); } } @@ -940,6 +942,8 @@ void Preprocessor::Lex(Token &Result) { StdCXXImportSeqState.handleHeaderName(); break; case tok::kw_export: + if (hasSeenNoTrivialPPDirective()) + Result.setFlag(Token::SeenNoTrivialPPDirective); TrackGMFState.handleExport(); StdCXXImportSeqState.handleExport(); ModuleDeclState.handleExport(); @@ -968,6 +972,8 @@ void Preprocessor::Lex(Token &Result) { } break; } else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) { + if (hasSeenNoTrivialPPDirective()) + Result.setFlag(Token::SeenNoTrivialPPDirective); TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq()); ModuleDeclState.handleModule(); break; @@ -1682,3 +1688,37 @@ const char *Preprocessor::getCheckPoint(FileID FID, const char *Start) const { return nullptr; } + +/// Whether allow C++ module directive. +bool Preprocessor::hasSeenNoTrivialPPDirective() const { + return DirTracer && DirTracer->hasSeenNoTrivialPPDirective(); +} + +bool TrivialDirectiveTracer::hasSeenNoTrivialPPDirective() const { + return SeenNoTrivialPPDirective; +} + +void TrivialDirectiveTracer::setSeenNoTrivialPPDirective(bool Val) { + if (InMainFile && !SeenNoTrivialPPDirective && Val) + SeenNoTrivialPPDirective = Val; +} + +void TrivialDirectiveTracer::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + setSeenNoTrivialPPDirective(false); +} + +void TrivialDirectiveTracer::LexedFileChanged( + FileID FID, LexedFileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, FileID PrevFID, SourceLocation Loc) { + InMainFile = FID == PP.getSourceManager().getMainFileID(); +} + +void TrivialDirectiveTracer::MacroExpands(const Token &MacroNameTok, + const MacroDefinition &MD, + SourceRange Range, + const MacroArgs *Args) { + setSeenNoTrivialPPDirective(!MD.getMacroInfo()->isBuiltinMacro()); +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index e57a789251a5b..6e0f22498a147 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -2363,9 +2363,10 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) { // Parse a global-module-fragment, if present. if (getLangOpts().CPlusPlusModules && Tok.is(tok::semi)) { SourceLocation SemiLoc = ConsumeToken(); - if (!Introducer.isFirstPPToken()) { + if (ImportState != Sema::ModuleImportState::FirstDecl || + Introducer.hasSeenNoTrivialPPDirective()) { Diag(StartLoc, diag::err_global_module_introducer_not_at_start) - << SourceRange(StartLoc, SemiLoc); + << SourceRange(StartLoc, SemiLoc); return nullptr; } if (MDK == Sema::ModuleDeclKind::Interface) { @@ -2420,7 +2421,8 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) { ExpectAndConsumeSemi(diag::err_module_expected_semi); return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition, - ImportState, Introducer.isFirstPPToken()); + ImportState, + Introducer.hasSeenNoTrivialPPDirective()); } Decl *Parser::ParseModuleImport(SourceLocation AtLoc, diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index ff9f85f960d93..1ecc5c747695f 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -265,10 +265,11 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleDeclKind MDK, ModuleIdPath Path, ModuleIdPath Partition, ModuleImportState &ImportState, - bool IntroducerIsFirstPPToken) { + bool SeenNoTrivialPPDirective) { assert(getLangOpts().CPlusPlusModules && "should only have module decl in standard C++ modules"); + bool IsFirstDecl = ImportState == ModuleImportState::FirstDecl; bool SeenGMF = ImportState == ModuleImportState::GlobalFragment; // If any of the steps here fail, we count that as invalidating C++20 // module state; @@ -336,7 +337,8 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, // In C++20, A module directive may only appear as the first preprocessing // tokens in a file (excluding the global module fragment.). - if (getLangOpts().CPlusPlusModules && !IntroducerIsFirstPPToken && !SeenGMF) { + if (getLangOpts().CPlusPlusModules && + (!IsFirstDecl || SeenNoTrivialPPDirective) && !SeenGMF) { Diag(ModuleLoc, diag::err_module_decl_not_at_start); SourceLocation BeginLoc = PP.getMainFileFirstPPTokenLoc(); Diag(BeginLoc, diag::note_global_module_introducer_missing) diff --git a/clang/test/CXX/module/cpp.pre/module_decl.cpp b/clang/test/CXX/module/cpp.pre/module_decl.cpp index 6238347c167ac..5c29aeff1b632 100644 --- a/clang/test/CXX/module/cpp.pre/module_decl.cpp +++ b/clang/test/CXX/module/cpp.pre/module_decl.cpp @@ -1,8 +1,147 @@ // RUN: rm -rf %t // RUN: mkdir -p %t -// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -verify -o %t/M.pcm +// RUN: split-file %s %t +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/line.cpp -verify -o %t/line.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/gnu_line_marker.cpp -verify -o %t/gnu_line_marker.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/include.cpp -verify -o %t/include.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/ident.cpp -verify -o %t/ident.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_comment.cpp -verify -o %t/pragma_comment.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_mark.cpp -verify -o %t/pragma_mark.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_detect_mismatch.cpp -verify -o %t/pragma_detect_mismatch.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_clang_debug.cpp -verify -o %t/pragma_clang_debug.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_message.cpp -verify -o %t/pragma_message.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_gcc_warn.cpp -verify -o %t/pragma_gcc_warn.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_gcc_error.cpp -verify -o %t/pragma_gcc_error.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_diag_push_pop.cpp -verify -o %t/pragma_diag_push_pop.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_diag_ignore.cpp -verify -o %t/pragma_diag_ignore.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_opencl_ext.cpp -verify -o %t/pragma_opencl_ext.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_push_pop.cpp -verify -o %t/pragma_push_pop.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_exec_charset.cpp -verify -o %t/pragma_exec_charset.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_clang_assume_nonnull.cpp -verify -o %t/pragma_clang_assume_nonnull.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/marco_expand.cpp -DMACRO="" -verify -o %t/marco_expand.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/define.cpp -verify -o %t/define.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/undef.cpp -verify -o %t/undef.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/defined.cpp -verify -o %t/defined.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/has_embed.cpp -verify -o %t/has_embed.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/has_include.cpp -verify -o %t/has_include.pcm +//--- header.h +#ifndef HEADER_H +#define HEADER_H + +#endif // HEADER_H + +//--- line.cpp +// expected-no-diagnostics +#line 3 +export module M; + +//--- gnu_line_marker.cpp +// expected-no-diagnostics +# 1 __FILE__ 1 3 +export module M; + +//--- include.cpp +#include "header.h" // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}} +export module M; // expected-error {{module declaration must occur at the start of the translation unit}} + +//--- ident.cpp +// expected-no-diagnostics +#ident "$Header:$" +export module M; + +//--- pragma_comment.cpp +// expected-no-diagnostics +#pragma comment(lib, "msvcrt.lib") +export module M; + +//--- pragma_mark.cpp +// expected-no-diagnostics +#pragma mark LLVM's world +export module M; + +//--- pragma_detect_mismatch.cpp +// expected-no-diagnostics +#pragma detect_mismatch("test", "1") +export module M; + +//--- pragma_clang_debug.cpp +// expected-no-diagnostics +#pragma clang __debug dump Test +export module M; + +//--- pragma_message.cpp +#pragma message "test" // expected-warning {{test}} +export module M; + +//--- pragma_gcc_warn.cpp +#pragma GCC warning "Foo" // expected-warning {{Foo}} +export module M; + +//--- pragma_gcc_error.cpp +#pragma GCC error "Foo" // expected-error {{Foo}} +export module M; + +//--- pragma_diag_push_pop.cpp +// expected-no-diagnostics +#pragma gcc diagnostic push +#pragma gcc diagnostic pop +export module M; + +//--- pragma_diag_ignore.cpp +// expected-no-diagnostics +#pragma GCC diagnostic ignored "-Wframe-larger-than" +export module M; + +//--- pragma_opencl_ext.cpp +// expected-no-diagnostics +#pragma OPENCL EXTENSION __cl_clang_variadic_functions : enable +export module M; + +//--- pragma_push_pop.cpp +// expected-no-diagnostics +#pragma warning(push) +#pragma warning(pop) +export module M; + +//--- pragma_exec_charset.cpp +// expected-no-diagnostics +#pragma execution_character_set(push, "UTF-8") +#pragma execution_character_set(pop) +export module M; + +//--- pragma_clang_assume_nonnull.cpp +// expected-no-diagnostics +#pragma clang assume_nonnull begin +#pragma clang assume_nonnull end +export module M; + +//--- marco_expand.cpp +MACRO // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}} +export module M; // expected-error {{module declaration must occur at the start of the translation unit}} + +//--- define.cpp // This is a comment #define I32 int // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}} export module M; // expected-error {{module declaration must occur at the start of the translation unit}} export I32 i32; + +//--- undef.cpp +#undef FOO // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}} +export module M; // expected-error {{module declaration must occur at the start of the translation unit}} + +//--- defined.cpp +#if defined(FOO) // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}} +#endif +export module M; // expected-error {{module declaration must occur at the start of the translation unit}} + +//--- has_embed.cpp +#if __has_embed(__FILE__ ext::token(0xB055)) // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}} +#endif +export module M; // expected-error {{module declaration must occur at the start of the translation unit}} + +//--- has_include.cpp +#if __has_include(<stdio.h>) || __has_include_next(<stdlib.h>) // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}} \ + // expected-warning {{#include_next in primary source file; will search from start of include path}} +#endif +export module M; // expected-error {{module declaration must occur at the start of the translation unit}} diff --git a/clang/unittests/Lex/LexerTest.cpp b/clang/unittests/Lex/LexerTest.cpp index 56d73cec1363f..c51cd0d2bfdaa 100644 --- a/clang/unittests/Lex/LexerTest.cpp +++ b/clang/unittests/Lex/LexerTest.cpp @@ -795,7 +795,7 @@ TEST_F(LexerTest, CheckFirstPPToken) { EXPECT_FALSE(Lexer::getRawToken(PP->getMainFileFirstPPTokenLoc(), Tok, PP->getSourceManager(), PP->getLangOpts(), /*IgnoreWhiteSpace=*/false)); - EXPECT_TRUE(Tok.isFirstPPToken()); + EXPECT_TRUE(PP->getMainFileFirstPPTokenLoc() == Tok.getLocation()); EXPECT_TRUE(Tok.is(tok::hash)); } @@ -811,7 +811,7 @@ TEST_F(LexerTest, CheckFirstPPToken) { EXPECT_FALSE(Lexer::getRawToken(PP->getMainFileFirstPPTokenLoc(), Tok, PP->getSourceManager(), PP->getLangOpts(), /*IgnoreWhiteSpace=*/false)); - EXPECT_TRUE(Tok.isFirstPPToken()); + EXPECT_TRUE(PP->getMainFileFirstPPTokenLoc() == Tok.getLocation()); EXPECT_TRUE(Tok.is(tok::raw_identifier)); EXPECT_TRUE(Tok.getRawIdentifier() == "FOO"); } >From abc6a89b6dcf78ecef9daf0d48eb3aa579ff9537 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Fri, 15 Aug 2025 12:11:49 +0800 Subject: [PATCH 2/7] Rename TrivialDirectiveTracer to TrivialPPDirectiveTracer and only handle no-trivial pp-directives Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/include/clang/Lex/Preprocessor.h | 6 +- clang/include/clang/Lex/Token.h | 5 +- ...iveTracer.h => TrivialPPDirectiveTracer.h} | 206 ++++++------------ clang/lib/Lex/Preprocessor.cpp | 38 ++-- 4 files changed, 86 insertions(+), 169 deletions(-) rename clang/include/clang/Lex/{TrivialDirectiveTracer.h => TrivialPPDirectiveTracer.h} (65%) diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index d51faad255224..e0036152e2e4f 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -82,7 +82,7 @@ class PreprocessorLexer; class PreprocessorOptions; class ScratchBuffer; class TargetInfo; -class TrivialDirectiveTracer; +class TrivialPPDirectiveTracer; namespace Builtin { class Context; @@ -357,7 +357,7 @@ class Preprocessor { /// A preprocessor directive tracer to trace whether the preprocessing /// state changed. These changes would mean most semantically observable /// preprocessor state, particularly anything that is order dependent. - TrivialDirectiveTracer *DirTracer = nullptr; + TrivialPPDirectiveTracer *DirTracer = nullptr; /// A position within a C++20 import-seq. class StdCXXImportSeq { @@ -3099,7 +3099,7 @@ class Preprocessor { bool setDeserializedSafeBufferOptOutMap( const SmallVectorImpl<SourceLocation> &SrcLocSeqs); - /// Whether allow C++ module directive. + /// Whether seen pp-directives which may change the preprocessing state. bool hasSeenNoTrivialPPDirective() const; private: diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h index c493571e00038..348670eb86629 100644 --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -89,7 +89,8 @@ class Token { IsReinjected = 0x800, // A phase 4 token that was produced before and // re-added, e.g. via EnterTokenStream. Annotation // tokens are *not* reinjected. - SeenNoTrivialPPDirective = 0x1000, + HasSeenNoTrivialPPDirective = + 0x1000, // Seen any 'no-trivial' pp-directives before current position. }; tok::TokenKind getKind() const { return Kind; } @@ -320,7 +321,7 @@ class Token { bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); } bool hasSeenNoTrivialPPDirective() const { - return getFlag(SeenNoTrivialPPDirective); + return getFlag(HasSeenNoTrivialPPDirective); } }; diff --git a/clang/include/clang/Lex/TrivialDirectiveTracer.h b/clang/include/clang/Lex/TrivialPPDirectiveTracer.h similarity index 65% rename from clang/include/clang/Lex/TrivialDirectiveTracer.h rename to clang/include/clang/Lex/TrivialPPDirectiveTracer.h index 9d4e0fdc96daf..a9f5fb0393fc7 100644 --- a/clang/include/clang/Lex/TrivialDirectiveTracer.h +++ b/clang/include/clang/Lex/TrivialPPDirectiveTracer.h @@ -1,4 +1,4 @@ -//===--- TrivialDirectiveTracer.h -------------------------------*- C++ -*-===// +//===--- TrivialPPDirectiveTracer.h -----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,39 +6,69 @@ // //===----------------------------------------------------------------------===// // -// This file defines the TrivialDirectiveTracer interface. +// This file defines the TrivialPPDirectiveTracer interface. // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_LEX_TRIVIAL_DIRECTIVE_TRACER_H -#define LLVM_CLANG_LEX_TRIVIAL_DIRECTIVE_TRACER_H +#ifndef LLVM_CLANG_LEX_TRIVIAL_PPDIRECTIVE_TRACER_H +#define LLVM_CLANG_LEX_TRIVIAL_PPDIRECTIVE_TRACER_H #include "clang/Lex/PPCallbacks.h" namespace clang { class Preprocessor; -class TrivialDirectiveTracer : public PPCallbacks { +/// Consider the following code: +/// +/// # 1 __FILE__ 1 3 +/// export module a; +/// +/// According to the wording in +/// [P1857R3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1857r3.html): +/// +/// A module directive may only appear as the first preprocessing tokens in a +/// file (excluding the global module fragment.) +/// +/// and the wording in +/// [[cpp.pre]](https://eel.is/c++draft/cpp.pre#nt:module-file): +/// module-file: +/// pp-global-module-fragment[opt] pp-module group[opt] +/// pp-private-module-fragment[opt] +/// +/// `#` is the first pp-token in the translation unit, and it was rejected by +/// clang, but they really should be exempted from this rule. The goal is to not +/// allow any preprocessor conditionals or most state changes, but these don't +/// fit that. +/// +/// State change would mean most semantically observable preprocessor state, +/// particularly anything that is order dependent. Global flags like being a +/// system header/module shouldn't matter. +/// +/// We should exempt a brunch of directives, even though it violates the current +/// standard wording. +/// +/// This class used to trace 'no-trivial' pp-directives in main file, which may +/// change the preprocessing state. +/// +/// FIXME: Once the wording of the standard is revised, we need to follow the +/// wording of the standard. Currently this is just a workaround +class TrivialPPDirectiveTracer : public PPCallbacks { Preprocessor &PP; + + /// Whether preprocessing main file. We only focus on the main file. bool InMainFile = true; + + /// Whether one or more conditional, include or other 'no-trivial' + /// pp-directives has seen before. bool SeenNoTrivialPPDirective = false; - void setSeenNoTrivialPPDirective(bool Val); + void setSeenNoTrivialPPDirective(); public: - TrivialDirectiveTracer(Preprocessor &P) : PP(P) {} + TrivialPPDirectiveTracer(Preprocessor &P) : PP(P) {} bool hasSeenNoTrivialPPDirective() const; - /// Callback invoked whenever a source file is entered or exited. - /// - /// \param Loc Indicates the new location. - /// \param PrevFID the file that was exited if \p Reason is ExitFile or the - /// the file before the new one entered for \p Reason EnterFile. - void FileChanged(SourceLocation Loc, FileChangeReason Reason, - SrcMgr::CharacteristicKind FileType, - FileID PrevFID = FileID()) override; - /// Callback invoked whenever the \p Lexer moves to a different file for /// lexing. Unlike \p FileChanged line number directives and other related /// pragmas do not trigger callbacks to \p LexedFileChanged. @@ -75,7 +105,7 @@ class TrivialDirectiveTracer : public PPCallbacks { void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled, OptionalFileEntryRef File, const LexEmbedParametersResult &Params) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Callback invoked whenever an inclusion directive of @@ -128,7 +158,7 @@ class TrivialDirectiveTracer : public PPCallbacks { StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported, SrcMgr::CharacteristicKind FileType) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Callback invoked whenever there was an explicit module-import @@ -143,126 +173,18 @@ class TrivialDirectiveTracer : public PPCallbacks { /// void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path, const Module *Imported) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Callback invoked when the end of the main file is reached. /// /// No subsequent callbacks will be made. - void EndOfMainFile() override { setSeenNoTrivialPPDirective(true); } - - /// Callback invoked when a \#ident or \#sccs directive is read. - /// \param Loc The location of the directive. - /// \param str The text of the directive. - /// - void Ident(SourceLocation Loc, StringRef str) override { - setSeenNoTrivialPPDirective(false); - } + void EndOfMainFile() override { setSeenNoTrivialPPDirective(); } /// Callback invoked when start reading any pragma directive. void PragmaDirective(SourceLocation Loc, PragmaIntroducerKind Introducer) override {} - /// Callback invoked when a \#pragma comment directive is read. - void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, - StringRef Str) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma mark comment is read. - void PragmaMark(SourceLocation Loc, StringRef Trivia) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma detect_mismatch directive is - /// read. - void PragmaDetectMismatch(SourceLocation Loc, StringRef Name, - StringRef Value) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma clang __debug directive is read. - /// \param Loc The location of the debug directive. - /// \param DebugType The identifier following __debug. - void PragmaDebug(SourceLocation Loc, StringRef DebugType) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma message directive is read. - /// \param Loc The location of the message directive. - /// \param Namespace The namespace of the message directive. - /// \param Kind The type of the message directive. - /// \param Str The text of the message directive. - void PragmaMessage(SourceLocation Loc, StringRef Namespace, - PragmaMessageKind Kind, StringRef Str) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma gcc diagnostic push directive - /// is read. - void PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma gcc diagnostic pop directive - /// is read. - void PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma gcc diagnostic directive is read. - void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, - diag::Severity mapping, StringRef Str) override { - setSeenNoTrivialPPDirective(false); - } - - /// Called when an OpenCL extension is either disabled or - /// enabled with a pragma. - void PragmaOpenCLExtension(SourceLocation NameLoc, const IdentifierInfo *Name, - SourceLocation StateLoc, unsigned State) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma warning directive is read. - void PragmaWarning(SourceLocation Loc, PragmaWarningSpecifier WarningSpec, - ArrayRef<int> Ids) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma warning(push) directive is read. - void PragmaWarningPush(SourceLocation Loc, int Level) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma warning(pop) directive is read. - void PragmaWarningPop(SourceLocation Loc) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma execution_character_set(push) directive - /// is read. - void PragmaExecCharsetPush(SourceLocation Loc, StringRef Str) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma execution_character_set(pop) directive - /// is read. - void PragmaExecCharsetPop(SourceLocation Loc) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma clang assume_nonnull begin directive - /// is read. - void PragmaAssumeNonNullBegin(SourceLocation Loc) override { - setSeenNoTrivialPPDirective(false); - } - - /// Callback invoked when a \#pragma clang assume_nonnull end directive - /// is read. - void PragmaAssumeNonNullEnd(SourceLocation Loc) override { - setSeenNoTrivialPPDirective(false); - } - /// Called by Preprocessor::HandleMacroExpandedIdentifier when a /// macro invocation is found. void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, @@ -271,7 +193,7 @@ class TrivialDirectiveTracer : public PPCallbacks { /// Hook called whenever a macro definition is seen. void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever a macro \#undef is seen. @@ -282,14 +204,14 @@ class TrivialDirectiveTracer : public PPCallbacks { /// MD is released immediately following this callback. void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, const MacroDirective *Undef) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever the 'defined' operator is seen. /// \param MD The MacroDirective if the name was a macro, null otherwise. void Defined(const Token &MacroNameTok, const MacroDefinition &MD, SourceRange Range) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#if is seen. @@ -300,7 +222,7 @@ class TrivialDirectiveTracer : public PPCallbacks { // FIXME: better to pass in a list (or tree!) of Tokens. void If(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind ConditionValue) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#elif is seen. @@ -311,7 +233,7 @@ class TrivialDirectiveTracer : public PPCallbacks { // FIXME: better to pass in a list (or tree!) of Tokens. void Elif(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind ConditionValue, SourceLocation IfLoc) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#ifdef is seen. @@ -320,7 +242,7 @@ class TrivialDirectiveTracer : public PPCallbacks { /// \param MD The MacroDefinition if the name was a macro, null otherwise. void Ifdef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#elifdef branch is taken. @@ -329,7 +251,7 @@ class TrivialDirectiveTracer : public PPCallbacks { /// \param MD The MacroDefinition if the name was a macro, null otherwise. void Elifdef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#elifdef is skipped. /// \param Loc the source location of the directive. @@ -338,7 +260,7 @@ class TrivialDirectiveTracer : public PPCallbacks { // FIXME: better to pass in a list (or tree!) of Tokens. void Elifdef(SourceLocation Loc, SourceRange ConditionRange, SourceLocation IfLoc) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#ifndef is seen. @@ -347,7 +269,7 @@ class TrivialDirectiveTracer : public PPCallbacks { /// \param MD The MacroDefiniton if the name was a macro, null otherwise. void Ifndef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#elifndef branch is taken. @@ -356,7 +278,7 @@ class TrivialDirectiveTracer : public PPCallbacks { /// \param MD The MacroDefinition if the name was a macro, null otherwise. void Elifndef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#elifndef is skipped. /// \param Loc the source location of the directive. @@ -365,24 +287,24 @@ class TrivialDirectiveTracer : public PPCallbacks { // FIXME: better to pass in a list (or tree!) of Tokens. void Elifndef(SourceLocation Loc, SourceRange ConditionRange, SourceLocation IfLoc) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#else is seen. /// \param Loc the source location of the directive. /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. void Else(SourceLocation Loc, SourceLocation IfLoc) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } /// Hook called whenever an \#endif is seen. /// \param Loc the source location of the directive. /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. void Endif(SourceLocation Loc, SourceLocation IfLoc) override { - setSeenNoTrivialPPDirective(true); + setSeenNoTrivialPPDirective(); } }; } // namespace clang -#endif // LLVM_CLANG_LEX_TRIVIAL_DIRECTIVE_TRACER_H +#endif // LLVM_CLANG_LEX_TRIVIAL_PPDIRECTIVE_TRACER_H diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index dd5ab0e19ecd4..9797c997301bb 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -50,7 +50,7 @@ #include "clang/Lex/ScratchBuffer.h" #include "clang/Lex/Token.h" #include "clang/Lex/TokenLexer.h" -#include "clang/Lex/TrivialDirectiveTracer.h" +#include "clang/Lex/TrivialPPDirectiveTracer.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -576,7 +576,7 @@ void Preprocessor::EnterMainSourceFile() { // export module M; // error: module declaration must occur // // at the start of the translation unit. if (getLangOpts().CPlusPlusModules) { - auto Tracer = std::make_unique<TrivialDirectiveTracer>(*this); + auto Tracer = std::make_unique<TrivialPPDirectiveTracer>(*this); DirTracer = Tracer.get(); addPPCallbacks(std::move(Tracer)); std::optional<Token> FirstPPTok = CurLexer->peekNextPPToken(); @@ -943,7 +943,7 @@ void Preprocessor::Lex(Token &Result) { break; case tok::kw_export: if (hasSeenNoTrivialPPDirective()) - Result.setFlag(Token::SeenNoTrivialPPDirective); + Result.setFlag(Token::HasSeenNoTrivialPPDirective); TrackGMFState.handleExport(); StdCXXImportSeqState.handleExport(); ModuleDeclState.handleExport(); @@ -973,7 +973,7 @@ void Preprocessor::Lex(Token &Result) { break; } else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) { if (hasSeenNoTrivialPPDirective()) - Result.setFlag(Token::SeenNoTrivialPPDirective); + Result.setFlag(Token::HasSeenNoTrivialPPDirective); TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq()); ModuleDeclState.handleModule(); break; @@ -1689,36 +1689,30 @@ const char *Preprocessor::getCheckPoint(FileID FID, const char *Start) const { return nullptr; } -/// Whether allow C++ module directive. bool Preprocessor::hasSeenNoTrivialPPDirective() const { return DirTracer && DirTracer->hasSeenNoTrivialPPDirective(); } -bool TrivialDirectiveTracer::hasSeenNoTrivialPPDirective() const { +bool TrivialPPDirectiveTracer::hasSeenNoTrivialPPDirective() const { return SeenNoTrivialPPDirective; } -void TrivialDirectiveTracer::setSeenNoTrivialPPDirective(bool Val) { - if (InMainFile && !SeenNoTrivialPPDirective && Val) - SeenNoTrivialPPDirective = Val; +void TrivialPPDirectiveTracer::setSeenNoTrivialPPDirective() { + if (InMainFile && !SeenNoTrivialPPDirective) + SeenNoTrivialPPDirective = true; } -void TrivialDirectiveTracer::FileChanged(SourceLocation Loc, - FileChangeReason Reason, - SrcMgr::CharacteristicKind FileType, - FileID PrevFID) { - setSeenNoTrivialPPDirective(false); -} - -void TrivialDirectiveTracer::LexedFileChanged( +void TrivialPPDirectiveTracer::LexedFileChanged( FileID FID, LexedFileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID, SourceLocation Loc) { InMainFile = FID == PP.getSourceManager().getMainFileID(); } -void TrivialDirectiveTracer::MacroExpands(const Token &MacroNameTok, - const MacroDefinition &MD, - SourceRange Range, - const MacroArgs *Args) { - setSeenNoTrivialPPDirective(!MD.getMacroInfo()->isBuiltinMacro()); +void TrivialPPDirectiveTracer::MacroExpands(const Token &MacroNameTok, + const MacroDefinition &MD, + SourceRange Range, + const MacroArgs *Args) { + // FIXME: Does only enable builtin macro expansion make sense? + if (!MD.getMacroInfo()->isBuiltinMacro()) + setSeenNoTrivialPPDirective(); } >From 7726cdcb96404492ca9d1b8ec087d75fab7ad592 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Fri, 15 Aug 2025 23:30:33 +0800 Subject: [PATCH 3/7] Address review comments and fix tests Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/include/clang/Lex/Token.h | 3 +- clang/lib/Lex/Preprocessor.cpp | 2 +- clang/unittests/Lex/ModuleDeclStateTest.cpp | 110 ++++++++------------ 3 files changed, 47 insertions(+), 68 deletions(-) diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h index 348670eb86629..d9dc5a562d802 100644 --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -90,7 +90,8 @@ class Token { // re-added, e.g. via EnterTokenStream. Annotation // tokens are *not* reinjected. HasSeenNoTrivialPPDirective = - 0x1000, // Seen any 'no-trivial' pp-directives before current position. + 0x1000, // Whether we've seen any 'no-trivial' pp-directives before + // current position. }; tok::TokenKind getKind() const { return Kind; } diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 9797c997301bb..165b3efd2fdef 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -1705,7 +1705,7 @@ void TrivialPPDirectiveTracer::setSeenNoTrivialPPDirective() { void TrivialPPDirectiveTracer::LexedFileChanged( FileID FID, LexedFileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID, SourceLocation Loc) { - InMainFile = FID == PP.getSourceManager().getMainFileID(); + InMainFile = (FID == PP.getSourceManager().getMainFileID()); } void TrivialPPDirectiveTracer::MacroExpands(const Token &MacroNameTok, diff --git a/clang/unittests/Lex/ModuleDeclStateTest.cpp b/clang/unittests/Lex/ModuleDeclStateTest.cpp index adc6cf1d2e598..1ae7d49c58f89 100644 --- a/clang/unittests/Lex/ModuleDeclStateTest.cpp +++ b/clang/unittests/Lex/ModuleDeclStateTest.cpp @@ -112,12 +112,10 @@ export module foo; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); EXPECT_TRUE(PP->isInNamedModule()); EXPECT_TRUE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -131,12 +129,10 @@ module foo; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); EXPECT_TRUE(PP->isInNamedModule()); EXPECT_FALSE(PP->isInNamedInterfaceUnit()); EXPECT_TRUE(PP->isInImplementationUnit()); @@ -150,12 +146,10 @@ module foo:part; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); EXPECT_TRUE(PP->isInNamedModule()); EXPECT_FALSE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -169,12 +163,10 @@ export module foo:part; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); EXPECT_TRUE(PP->isInNamedModule()); EXPECT_TRUE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -188,12 +180,10 @@ export module foo.dot:part.dot; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); EXPECT_TRUE(PP->isInNamedModule()); EXPECT_TRUE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -207,12 +197,10 @@ TEST_F(ModuleDeclStateTest, NotModule) { std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)0); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); EXPECT_FALSE(PP->isInNamedModule()); EXPECT_FALSE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -233,12 +221,10 @@ import :another; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {true, true}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)2); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)2); EXPECT_TRUE(PP->isInNamedModule()); EXPECT_TRUE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -260,12 +246,10 @@ import :another; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {true, true}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)2); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)2); EXPECT_TRUE(PP->isInNamedModule()); EXPECT_TRUE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -286,12 +270,10 @@ import :another; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {true}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)1); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)1); EXPECT_FALSE(PP->isInNamedModule()); EXPECT_FALSE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -304,12 +286,10 @@ TEST_F(ModuleDeclStateTest, ImportAClangNamedModule) { std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::ObjCXX); std::initializer_list<bool> ImportKinds = {false}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)1); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)1); EXPECT_FALSE(PP->isInNamedModule()); EXPECT_FALSE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); @@ -326,12 +306,10 @@ import M2; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::ObjCXX); std::initializer_list<bool> ImportKinds = {false, true, false, true}; - preprocess(*PP, - std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds)); - - auto *Callback = - static_cast<CheckNamedModuleImportingCB *>(PP->getPPCallbacks()); - EXPECT_EQ(Callback->importNamedModuleNum(), (size_t)4); + auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); + preprocess(*PP, std::move(Callback)); + EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)4); EXPECT_FALSE(PP->isInNamedModule()); EXPECT_FALSE(PP->isInNamedInterfaceUnit()); EXPECT_FALSE(PP->isInImplementationUnit()); >From 20bd6dd24a249bdc9735612da46a54db0c2bfab7 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Fri, 15 Aug 2025 23:32:19 +0800 Subject: [PATCH 4/7] Fix comments Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/include/clang/Lex/Preprocessor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index e0036152e2e4f..8bf5ac3278b2d 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -3099,7 +3099,7 @@ class Preprocessor { bool setDeserializedSafeBufferOptOutMap( const SmallVectorImpl<SourceLocation> &SrcLocSeqs); - /// Whether seen pp-directives which may change the preprocessing state. + /// Whether we've seen pp-directives which may have changed the preprocessing state. bool hasSeenNoTrivialPPDirective() const; private: >From 24c61829d0cb779f091005691e8ccd49a45cea3d Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Fri, 15 Aug 2025 23:38:02 +0800 Subject: [PATCH 5/7] Format Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/unittests/Lex/ModuleDeclStateTest.cpp | 40 +++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/clang/unittests/Lex/ModuleDeclStateTest.cpp b/clang/unittests/Lex/ModuleDeclStateTest.cpp index 1ae7d49c58f89..ac2ddfaf52cd0 100644 --- a/clang/unittests/Lex/ModuleDeclStateTest.cpp +++ b/clang/unittests/Lex/ModuleDeclStateTest.cpp @@ -61,14 +61,15 @@ class ModuleDeclStateTest : public ::testing::Test { Target = TargetInfo::CreateTargetInfo(Diags, *TargetOpts); } - std::unique_ptr<Preprocessor> - getPreprocessor(const char *source, Language Lang) { + std::unique_ptr<Preprocessor> getPreprocessor(const char *source, + Language Lang) { std::unique_ptr<llvm::MemoryBuffer> Buf = llvm::MemoryBuffer::getMemBuffer(source); SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); std::vector<std::string> Includes; - LangOptions::setLangDefaults(LangOpts, Lang, Target->getTriple(), Includes, LangStandard::lang_cxx20); + LangOptions::setLangDefaults(LangOpts, Lang, Target->getTriple(), Includes, + LangStandard::lang_cxx20); LangOpts.CPlusPlusModules = true; if (Lang != Language::CXX) { LangOpts.Modules = true; @@ -112,7 +113,8 @@ export module foo; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); @@ -129,7 +131,8 @@ module foo; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); @@ -146,7 +149,8 @@ module foo:part; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); @@ -163,7 +167,8 @@ export module foo:part; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); @@ -180,7 +185,8 @@ export module foo.dot:part.dot; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); @@ -197,7 +203,8 @@ TEST_F(ModuleDeclStateTest, NotModule) { std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)0); @@ -221,7 +228,8 @@ import :another; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {true, true}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)2); @@ -246,7 +254,8 @@ import :another; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {true, true}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)2); @@ -270,7 +279,8 @@ import :another; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); std::initializer_list<bool> ImportKinds = {true}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)1); @@ -286,7 +296,8 @@ TEST_F(ModuleDeclStateTest, ImportAClangNamedModule) { std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::ObjCXX); std::initializer_list<bool> ImportKinds = {false}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)1); @@ -306,7 +317,8 @@ import M2; std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::ObjCXX); std::initializer_list<bool> ImportKinds = {false, true, false, true}; - auto Callback = std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); + auto Callback = + std::make_unique<CheckNamedModuleImportingCB>(*PP, ImportKinds); CheckNamedModuleImportingCB *CallbackPtr = Callback.get(); preprocess(*PP, std::move(Callback)); EXPECT_EQ(CallbackPtr->importNamedModuleNum(), (size_t)4); >From cf9c745e32e1185f6e93abb47a21d47010662e10 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Fri, 15 Aug 2025 23:47:16 +0800 Subject: [PATCH 6/7] Format Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/include/clang/Lex/Preprocessor.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 8bf5ac3278b2d..f204c69c51455 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -3099,7 +3099,8 @@ class Preprocessor { bool setDeserializedSafeBufferOptOutMap( const SmallVectorImpl<SourceLocation> &SrcLocSeqs); - /// Whether we've seen pp-directives which may have changed the preprocessing state. + /// Whether we've seen pp-directives which may have changed the preprocessing + /// state. bool hasSeenNoTrivialPPDirective() const; private: >From b0277063387946ead32cb4b9a0421c626e1861f2 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Sat, 16 Aug 2025 19:40:17 +0800 Subject: [PATCH 7/7] Rename TrivialPPDirectiveTracer to NoTrivialPPDirectiveTracer and add unit tests Signed-off-by: yronglin <yronglin...@gmail.com> --- ...eTracer.h => NoTrivialPPDirectiveTracer.h} | 14 +- clang/include/clang/Lex/Preprocessor.h | 4 +- clang/lib/Lex/Preprocessor.cpp | 18 +- clang/unittests/Lex/CMakeLists.txt | 1 + .../Lex/NoTrivialPPDirectiveTracerTest.cpp | 182 ++++++++++++++++++ 5 files changed, 201 insertions(+), 18 deletions(-) rename clang/include/clang/Lex/{TrivialPPDirectiveTracer.h => NoTrivialPPDirectiveTracer.h} (96%) create mode 100644 clang/unittests/Lex/NoTrivialPPDirectiveTracerTest.cpp diff --git a/clang/include/clang/Lex/TrivialPPDirectiveTracer.h b/clang/include/clang/Lex/NoTrivialPPDirectiveTracer.h similarity index 96% rename from clang/include/clang/Lex/TrivialPPDirectiveTracer.h rename to clang/include/clang/Lex/NoTrivialPPDirectiveTracer.h index a9f5fb0393fc7..9ab3c6a528a1a 100644 --- a/clang/include/clang/Lex/TrivialPPDirectiveTracer.h +++ b/clang/include/clang/Lex/NoTrivialPPDirectiveTracer.h @@ -1,4 +1,4 @@ -//===--- TrivialPPDirectiveTracer.h -----------------------------*- C++ -*-===// +//===--- NoTrivialPPDirectiveTracer.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// // -// This file defines the TrivialPPDirectiveTracer interface. +// This file defines the NoTrivialPPDirectiveTracer interface. // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_LEX_TRIVIAL_PPDIRECTIVE_TRACER_H -#define LLVM_CLANG_LEX_TRIVIAL_PPDIRECTIVE_TRACER_H +#ifndef LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H +#define LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H #include "clang/Lex/PPCallbacks.h" @@ -52,7 +52,7 @@ class Preprocessor; /// /// FIXME: Once the wording of the standard is revised, we need to follow the /// wording of the standard. Currently this is just a workaround -class TrivialPPDirectiveTracer : public PPCallbacks { +class NoTrivialPPDirectiveTracer : public PPCallbacks { Preprocessor &PP; /// Whether preprocessing main file. We only focus on the main file. @@ -65,7 +65,7 @@ class TrivialPPDirectiveTracer : public PPCallbacks { void setSeenNoTrivialPPDirective(); public: - TrivialPPDirectiveTracer(Preprocessor &P) : PP(P) {} + NoTrivialPPDirectiveTracer(Preprocessor &P) : PP(P) {} bool hasSeenNoTrivialPPDirective() const; @@ -307,4 +307,4 @@ class TrivialPPDirectiveTracer : public PPCallbacks { } // namespace clang -#endif // LLVM_CLANG_LEX_TRIVIAL_PPDIRECTIVE_TRACER_H +#endif // LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index f204c69c51455..39754847a93e4 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -82,7 +82,7 @@ class PreprocessorLexer; class PreprocessorOptions; class ScratchBuffer; class TargetInfo; -class TrivialPPDirectiveTracer; +class NoTrivialPPDirectiveTracer; namespace Builtin { class Context; @@ -357,7 +357,7 @@ class Preprocessor { /// A preprocessor directive tracer to trace whether the preprocessing /// state changed. These changes would mean most semantically observable /// preprocessor state, particularly anything that is order dependent. - TrivialPPDirectiveTracer *DirTracer = nullptr; + NoTrivialPPDirectiveTracer *DirTracer = nullptr; /// A position within a C++20 import-seq. class StdCXXImportSeq { diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 165b3efd2fdef..e003ad3a95570 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -43,6 +43,7 @@ #include "clang/Lex/MacroArgs.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/ModuleLoader.h" +#include "clang/Lex/NoTrivialPPDirectiveTracer.h" #include "clang/Lex/Pragma.h" #include "clang/Lex/PreprocessingRecord.h" #include "clang/Lex/PreprocessorLexer.h" @@ -50,7 +51,6 @@ #include "clang/Lex/ScratchBuffer.h" #include "clang/Lex/Token.h" #include "clang/Lex/TokenLexer.h" -#include "clang/Lex/TrivialPPDirectiveTracer.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -576,7 +576,7 @@ void Preprocessor::EnterMainSourceFile() { // export module M; // error: module declaration must occur // // at the start of the translation unit. if (getLangOpts().CPlusPlusModules) { - auto Tracer = std::make_unique<TrivialPPDirectiveTracer>(*this); + auto Tracer = std::make_unique<NoTrivialPPDirectiveTracer>(*this); DirTracer = Tracer.get(); addPPCallbacks(std::move(Tracer)); std::optional<Token> FirstPPTok = CurLexer->peekNextPPToken(); @@ -1693,25 +1693,25 @@ bool Preprocessor::hasSeenNoTrivialPPDirective() const { return DirTracer && DirTracer->hasSeenNoTrivialPPDirective(); } -bool TrivialPPDirectiveTracer::hasSeenNoTrivialPPDirective() const { +bool NoTrivialPPDirectiveTracer::hasSeenNoTrivialPPDirective() const { return SeenNoTrivialPPDirective; } -void TrivialPPDirectiveTracer::setSeenNoTrivialPPDirective() { +void NoTrivialPPDirectiveTracer::setSeenNoTrivialPPDirective() { if (InMainFile && !SeenNoTrivialPPDirective) SeenNoTrivialPPDirective = true; } -void TrivialPPDirectiveTracer::LexedFileChanged( +void NoTrivialPPDirectiveTracer::LexedFileChanged( FileID FID, LexedFileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID, SourceLocation Loc) { InMainFile = (FID == PP.getSourceManager().getMainFileID()); } -void TrivialPPDirectiveTracer::MacroExpands(const Token &MacroNameTok, - const MacroDefinition &MD, - SourceRange Range, - const MacroArgs *Args) { +void NoTrivialPPDirectiveTracer::MacroExpands(const Token &MacroNameTok, + const MacroDefinition &MD, + SourceRange Range, + const MacroArgs *Args) { // FIXME: Does only enable builtin macro expansion make sense? if (!MD.getMacroInfo()->isBuiltinMacro()) setSeenNoTrivialPPDirective(); diff --git a/clang/unittests/Lex/CMakeLists.txt b/clang/unittests/Lex/CMakeLists.txt index 96ca6dda9cd85..fa5e58f5a8932 100644 --- a/clang/unittests/Lex/CMakeLists.txt +++ b/clang/unittests/Lex/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_unittest(LexTests LexerTest.cpp LexHLSLRootSignatureTest.cpp ModuleDeclStateTest.cpp + NoTrivialPPDirectiveTracerTest.cpp PPCallbacksTest.cpp PPConditionalDirectiveRecordTest.cpp PPDependencyDirectivesTest.cpp diff --git a/clang/unittests/Lex/NoTrivialPPDirectiveTracerTest.cpp b/clang/unittests/Lex/NoTrivialPPDirectiveTracerTest.cpp new file mode 100644 index 0000000000000..d79c1428e55bb --- /dev/null +++ b/clang/unittests/Lex/NoTrivialPPDirectiveTracerTest.cpp @@ -0,0 +1,182 @@ +//===- unittests/Lex/NoTrivialPPDirectiveTracerTest.cpp -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Lex/ModuleLoader.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "gtest/gtest.h" +#include <cstddef> +#include <initializer_list> + +using namespace clang; + +namespace { +class NoTrivialPPDirectiveTracerTest : public ::testing::Test { +protected: + NoTrivialPPDirectiveTracerTest() + : VFS(llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>()), + FileMgr(FileMgrOpts, VFS), + Diags(DiagnosticIDs::create(), DiagOpts, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) { + TargetOpts->Triple = "x86_64-unknown-linux-gnu"; + Target = TargetInfo::CreateTargetInfo(Diags, *TargetOpts); + } + + void addFile(const char *source, StringRef Filename) { + VFS->addFile(Filename, 0, llvm::MemoryBuffer::getMemBuffer(source), + /*User=*/std::nullopt, + /*Group=*/std::nullopt, + llvm::sys::fs::file_type::regular_file); + } + + std::unique_ptr<Preprocessor> getPreprocessor(const char *source, + Language Lang) { + std::unique_ptr<llvm::MemoryBuffer> Buf = + llvm::MemoryBuffer::getMemBuffer(source); + SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); + + std::vector<std::string> Includes; + LangOptions::setLangDefaults(LangOpts, Lang, Target->getTriple(), Includes, + LangStandard::lang_cxx20); + LangOpts.CPlusPlusModules = true; + if (Lang != Language::CXX) { + LangOpts.Modules = true; + LangOpts.ImplicitModules = true; + } + + HeaderInfo.emplace(HSOpts, SourceMgr, Diags, LangOpts, Target.get()); + + auto DE = FileMgr.getOptionalDirectoryRef("."); + assert(DE); + auto DL = DirectoryLookup(*DE, SrcMgr::C_User, /*isFramework=*/false); + HeaderInfo->AddSearchPath(DL, /*isAngled=*/false); + + return std::make_unique<Preprocessor>(PPOpts, Diags, LangOpts, SourceMgr, + *HeaderInfo, ModLoader, + /*IILookup=*/nullptr, + /*OwnsHeaderSearch=*/false); + } + + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; + FileSystemOptions FileMgrOpts; + FileManager FileMgr; + DiagnosticOptions DiagOpts; + DiagnosticsEngine Diags; + SourceManager SourceMgr; + std::shared_ptr<TargetOptions> TargetOpts; + IntrusiveRefCntPtr<TargetInfo> Target; + LangOptions LangOpts; + TrivialModuleLoader ModLoader; + HeaderSearchOptions HSOpts; + std::optional<HeaderSearch> HeaderInfo; + PreprocessorOptions PPOpts; +}; + +TEST_F(NoTrivialPPDirectiveTracerTest, TrivialDirective) { + const char *source = R"( + #line 7 + # 1 __FILE__ 1 3 + #ident "$Header:$" + #pragma comment(lib, "msvcrt.lib") + #pragma mark LLVM's world + #pragma detect_mismatch("test", "1") + #pragma clang __debug dump Test + #pragma message "test" + #pragma GCC warning "Foo" + #pragma GCC error "Foo" + #pragma gcc diagnostic push + #pragma gcc diagnostic pop + #pragma GCC diagnostic ignored "-Wframe-larger-than" + #pragma OPENCL EXTENSION __cl_clang_variadic_functions : enable + #pragma warning(push) + #pragma warning(pop) + #pragma execution_character_set(push, "UTF-8") + #pragma execution_character_set(pop) + #pragma clang assume_nonnull begin + #pragma clang assume_nonnull end + int foo; + )"; + std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); + PP->Initialize(*Target); + PP->EnterMainSourceFile(); + Token Tok; + PP->Lex(Tok); + EXPECT_FALSE(PP->hasSeenNoTrivialPPDirective()); +} + +TEST_F(NoTrivialPPDirectiveTracerTest, IncludeDirective) { + const char *source = R"( + #include "header.h" + int foo; + )"; + const char *header = R"( + #ifndef HEADER_H + #define HEADER_H + #endif // HEADER_H + )"; + std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); + addFile(header, "header.h"); + PP->Initialize(*Target); + PP->EnterMainSourceFile(); + Token Tok; + PP->Lex(Tok); + EXPECT_TRUE(PP->hasSeenNoTrivialPPDirective()); +} + +TEST_F(NoTrivialPPDirectiveTracerTest, DefineDirective) { + const char *source = R"( + #define FOO + int foo; + )"; + std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); + PP->Initialize(*Target); + PP->EnterMainSourceFile(); + Token Tok; + PP->Lex(Tok); + EXPECT_TRUE(PP->hasSeenNoTrivialPPDirective()); +} + +TEST_F(NoTrivialPPDirectiveTracerTest, UnDefineDirective) { + const char *source = R"( + #undef FOO + int foo; + )"; + std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); + PP->Initialize(*Target); + PP->setPredefines("#define FOO"); + PP->EnterMainSourceFile(); + Token Tok; + PP->Lex(Tok); + EXPECT_TRUE(PP->hasSeenNoTrivialPPDirective()); +} + +TEST_F(NoTrivialPPDirectiveTracerTest, IfDefinedDirective) { + const char *source = R"( + #if defined(FOO) + #endif + int foo; + )"; + std::unique_ptr<Preprocessor> PP = getPreprocessor(source, Language::CXX); + PP->Initialize(*Target); + PP->setPredefines("#define FOO"); + PP->EnterMainSourceFile(); + Token Tok; + PP->Lex(Tok); + EXPECT_TRUE(PP->hasSeenNoTrivialPPDirective()); +} + +} // namespace _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits