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/2] [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/2] 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(); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits