diff options
Diffstat (limited to 'contrib/llvm-project/clang/include/clang/Lex/Preprocessor.h')
-rw-r--r-- | contrib/llvm-project/clang/include/clang/Lex/Preprocessor.h | 706 |
1 files changed, 618 insertions, 88 deletions
diff --git a/contrib/llvm-project/clang/include/clang/Lex/Preprocessor.h b/contrib/llvm-project/clang/include/clang/Lex/Preprocessor.h index fe2327f0a480..b0a8ec0fec5e 100644 --- a/contrib/llvm-project/clang/include/clang/Lex/Preprocessor.h +++ b/contrib/llvm-project/clang/include/clang/Lex/Preprocessor.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_LEX_PREPROCESSOR_H #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" @@ -22,20 +23,18 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TokenKinds.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/ModuleMap.h" #include "clang/Lex/PPCallbacks.h" -#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h" #include "clang/Lex/Token.h" #include "clang/Lex/TokenLexer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/FunctionExtras.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" @@ -51,6 +50,7 @@ #include <cstdint> #include <map> #include <memory> +#include <optional> #include <string> #include <utility> #include <vector> @@ -66,7 +66,6 @@ namespace clang { class CodeCompletionHandler; class CommentHandler; class DirectoryEntry; -class DirectoryLookup; class EmptylineHandler; class ExternalPreprocessorSource; class FileEntry; @@ -133,7 +132,7 @@ class Preprocessor { llvm::unique_function<void(const clang::Token &)> OnToken; std::shared_ptr<PreprocessorOptions> PPOpts; DiagnosticsEngine *Diags; - LangOptions &LangOpts; + const LangOptions &LangOpts; const TargetInfo *Target = nullptr; const TargetInfo *AuxTarget = nullptr; FileManager &FileMgr; @@ -164,6 +163,7 @@ class Preprocessor { IdentifierInfo *Ident__has_feature; // __has_feature IdentifierInfo *Ident__has_extension; // __has_extension IdentifierInfo *Ident__has_builtin; // __has_builtin + IdentifierInfo *Ident__has_constexpr_builtin; // __has_constexpr_builtin IdentifierInfo *Ident__has_attribute; // __has_attribute IdentifierInfo *Ident__has_include; // __has_include IdentifierInfo *Ident__has_include_next; // __has_include_next @@ -178,12 +178,29 @@ class Preprocessor { IdentifierInfo *Ident__is_target_vendor; // __is_target_vendor IdentifierInfo *Ident__is_target_os; // __is_target_os IdentifierInfo *Ident__is_target_environment; // __is_target_environment + IdentifierInfo *Ident__is_target_variant_os; + IdentifierInfo *Ident__is_target_variant_environment; + IdentifierInfo *Ident__FLT_EVAL_METHOD__; // __FLT_EVAL_METHOD // Weak, only valid (and set) while InMacroArgs is true. Token* ArgMacro; SourceLocation DATELoc, TIMELoc; + // FEM_UnsetOnCommandLine means that an explicit evaluation method was + // not specified on the command line. The target is queried to set the + // default evaluation method. + LangOptions::FPEvalMethodKind CurrentFPEvalMethod = + LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine; + + // The most recent pragma location where the floating point evaluation + // method was modified. This is used to determine whether the + // 'pragma clang fp eval_method' was used whithin the current scope. + SourceLocation LastFPEvalPragmaLocation; + + LangOptions::FPEvalMethodKind TUFPEvalMethod = + LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine; + // Next __COUNTER__ value, starts at 0. unsigned CounterValue = 0; @@ -260,8 +277,7 @@ class Preprocessor { /// Empty line handler. EmptylineHandler *Emptyline = nullptr; - /// True if we want to ignore EOF token and continue later on (thus - /// avoid tearing the Lexer and etc. down). + /// True to avoid tearing down the lexer etc on EOF bool IncrementalProcessing = false; public: @@ -292,14 +308,17 @@ private: /// lexed, if any. SourceLocation ModuleImportLoc; - /// The module import path that we're currently processing. - SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> ModuleImportPath; + /// The import path for named module that we're currently processing. + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> NamedModuleImportPath; + + /// Whether the import is an `@import` or a standard c++ modules import. + bool IsAtImport = false; /// Whether the last token we lexed was an '@'. bool LastTokenWasAt = false; /// A position within a C++20 import-seq. - class ImportSeq { + class StdCXXImportSeq { public: enum State : int { // Positive values represent a number of unclosed brackets. @@ -309,7 +328,7 @@ private: AfterImportSeq = -3, }; - ImportSeq(State S) : S(S) {} + StdCXXImportSeq(State S) : S(S) {} /// Saw any kind of open bracket. void handleOpenBracket() { @@ -364,6 +383,7 @@ private: bool atTopLevel() { return S <= 0; } bool afterImportSeq() { return S == AfterImportSeq; } + bool afterTopLevelSeq() { return S == AfterTopLevelTokenSeq; } private: State S; @@ -374,7 +394,206 @@ private: }; /// Our current position within a C++20 import-seq. - ImportSeq ImportSeqState = ImportSeq::AfterTopLevelTokenSeq; + StdCXXImportSeq StdCXXImportSeqState = StdCXXImportSeq::AfterTopLevelTokenSeq; + + /// Track whether we are in a Global Module Fragment + class TrackGMF { + public: + enum GMFState : int { + GMFActive = 1, + MaybeGMF = 0, + BeforeGMFIntroducer = -1, + GMFAbsentOrEnded = -2, + }; + + TrackGMF(GMFState S) : S(S) {} + + /// Saw a semicolon. + void handleSemi() { + // If it is immediately after the first instance of the module keyword, + // then that introduces the GMF. + if (S == MaybeGMF) + S = GMFActive; + } + + /// Saw an 'export' identifier. + void handleExport() { + // The presence of an 'export' keyword always ends or excludes a GMF. + S = GMFAbsentOrEnded; + } + + /// Saw an 'import' identifier. + void handleImport(bool AfterTopLevelTokenSeq) { + // If we see this before any 'module' kw, then we have no GMF. + if (AfterTopLevelTokenSeq && S == BeforeGMFIntroducer) + S = GMFAbsentOrEnded; + } + + /// Saw a 'module' identifier. + void handleModule(bool AfterTopLevelTokenSeq) { + // This was the first module identifier and not preceded by any token + // that would exclude a GMF. It could begin a GMF, but only if directly + // followed by a semicolon. + if (AfterTopLevelTokenSeq && S == BeforeGMFIntroducer) + S = MaybeGMF; + else + S = GMFAbsentOrEnded; + } + + /// Saw any other token. + void handleMisc() { + // We saw something other than ; after the 'module' kw, so not a GMF. + if (S == MaybeGMF) + S = GMFAbsentOrEnded; + } + + bool inGMF() { return S == GMFActive; } + + private: + /// Track the transitions into and out of a Global Module Fragment, + /// if one is present. + GMFState S; + }; + + TrackGMF TrackGMFState = TrackGMF::BeforeGMFIntroducer; + + /// Track the status of the c++20 module decl. + /// + /// module-declaration: + /// 'export'[opt] 'module' module-name module-partition[opt] + /// attribute-specifier-seq[opt] ';' + /// + /// module-name: + /// module-name-qualifier[opt] identifier + /// + /// module-partition: + /// ':' module-name-qualifier[opt] identifier + /// + /// module-name-qualifier: + /// identifier '.' + /// module-name-qualifier identifier '.' + /// + /// Transition state: + /// + /// NotAModuleDecl --- export ---> FoundExport + /// NotAModuleDecl --- module ---> ImplementationCandidate + /// FoundExport --- module ---> InterfaceCandidate + /// ImplementationCandidate --- Identifier ---> ImplementationCandidate + /// ImplementationCandidate --- period ---> ImplementationCandidate + /// ImplementationCandidate --- colon ---> ImplementationCandidate + /// InterfaceCandidate --- Identifier ---> InterfaceCandidate + /// InterfaceCandidate --- period ---> InterfaceCandidate + /// InterfaceCandidate --- colon ---> InterfaceCandidate + /// ImplementationCandidate --- Semi ---> NamedModuleImplementation + /// NamedModuleInterface --- Semi ---> NamedModuleInterface + /// NamedModuleImplementation --- Anything ---> NamedModuleImplementation + /// NamedModuleInterface --- Anything ---> NamedModuleInterface + /// + /// FIXME: We haven't handle attribute-specifier-seq here. It may not be bad + /// soon since we don't support any module attributes yet. + class ModuleDeclSeq { + enum ModuleDeclState : int { + NotAModuleDecl, + FoundExport, + InterfaceCandidate, + ImplementationCandidate, + NamedModuleInterface, + NamedModuleImplementation, + }; + + public: + ModuleDeclSeq() = default; + + void handleExport() { + if (State == NotAModuleDecl) + State = FoundExport; + else if (!isNamedModule()) + reset(); + } + + void handleModule() { + if (State == FoundExport) + State = InterfaceCandidate; + else if (State == NotAModuleDecl) + State = ImplementationCandidate; + else if (!isNamedModule()) + reset(); + } + + void handleIdentifier(IdentifierInfo *Identifier) { + if (isModuleCandidate() && Identifier) + Name += Identifier->getName().str(); + else if (!isNamedModule()) + reset(); + } + + void handleColon() { + if (isModuleCandidate()) + Name += ":"; + else if (!isNamedModule()) + reset(); + } + + void handlePeriod() { + if (isModuleCandidate()) + Name += "."; + else if (!isNamedModule()) + reset(); + } + + void handleSemi() { + if (!Name.empty() && isModuleCandidate()) { + if (State == InterfaceCandidate) + State = NamedModuleInterface; + else if (State == ImplementationCandidate) + State = NamedModuleImplementation; + else + llvm_unreachable("Unimaged ModuleDeclState."); + } else if (!isNamedModule()) + reset(); + } + + void handleMisc() { + if (!isNamedModule()) + reset(); + } + + bool isModuleCandidate() const { + return State == InterfaceCandidate || State == ImplementationCandidate; + } + + bool isNamedModule() const { + return State == NamedModuleInterface || + State == NamedModuleImplementation; + } + + bool isNamedInterface() const { return State == NamedModuleInterface; } + + bool isImplementationUnit() const { + return State == NamedModuleImplementation && !getName().contains(':'); + } + + StringRef getName() const { + assert(isNamedModule() && "Can't get name from a non named module"); + return Name; + } + + StringRef getPrimaryName() const { + assert(isNamedModule() && "Can't get name from a non named module"); + return getName().split(':').first; + } + + void reset() { + Name.clear(); + State = NotAModuleDecl; + } + + private: + ModuleDeclState State = NotAModuleDecl; + std::string Name; + }; + + ModuleDeclSeq ModuleDeclState; /// Whether the module import expects an identifier next. Otherwise, /// it expects a '.' or ';'. @@ -388,6 +607,14 @@ private: /// \#pragma clang assume_nonnull begin. SourceLocation PragmaAssumeNonNullLoc; + /// Set only for preambles which end with an active + /// \#pragma clang assume_nonnull begin. + /// + /// When the preamble is loaded into the main file, + /// `PragmaAssumeNonNullLoc` will be set to this to + /// replay the unterminated assume_nonnull. + SourceLocation PreambleRecordedPragmaAssumeNonNullLoc; + /// True if we hit the code-completion point. bool CodeCompletionReached = false; @@ -401,7 +628,7 @@ private: /// The directory that the main file should be considered to occupy, /// if it does not correspond to a real file (as happens when building a /// module). - const DirectoryEntry *MainFileDir = nullptr; + OptionalDirectoryEntryRef MainFileDir; /// The number of bytes that we will initially skip when entering the /// main file, along with a flag that indicates whether skipping this number @@ -449,6 +676,8 @@ public: ElseLoc(ElseLoc) {} }; + using IncludedFilesSet = llvm::DenseSet<const FileEntry *>; + private: friend class ASTReader; friend class MacroArgs; @@ -486,11 +715,11 @@ private: bool hasRecordedPreamble() const { return !ConditionalStack.empty(); } - bool reachedEOFWhileSkipping() const { return SkipInfo.hasValue(); } + bool reachedEOFWhileSkipping() const { return SkipInfo.has_value(); } void clearSkipInfo() { SkipInfo.reset(); } - llvm::Optional<PreambleSkipInfo> SkipInfo; + std::optional<PreambleSkipInfo> SkipInfo; private: SmallVector<PPConditionalInfo, 4> ConditionalStack; @@ -503,7 +732,7 @@ private: /// Only one of CurLexer, or CurTokenLexer will be non-null. std::unique_ptr<Lexer> CurLexer; - /// The current top of the stack what we're lexing from + /// The current top of the stack that we're lexing from /// if not expanding a macro. /// /// This is an alias for CurLexer. @@ -514,7 +743,7 @@ private: /// /// This allows us to implement \#include_next and find directory-specific /// properties. - const DirectoryLookup *CurDirLookup = nullptr; + ConstSearchDirIterator CurDirLookup = nullptr; /// The current macro we are expanding, if we are expanding a macro. /// @@ -522,12 +751,8 @@ private: std::unique_ptr<TokenLexer> CurTokenLexer; /// The kind of lexer we're currently working with. - enum CurLexerKind { - CLK_Lexer, - CLK_TokenLexer, - CLK_CachingLexer, - CLK_LexAfterModuleImport - } CurLexerKind = CLK_Lexer; + typedef bool (*LexerCallback)(Preprocessor &, Token &); + LexerCallback CurLexerCallback = &CLK_Lexer; /// If the current lexer is for a submodule that is being built, this /// is that submodule. @@ -537,21 +762,21 @@ private: /// \#included, and macros currently being expanded from, not counting /// CurLexer/CurTokenLexer. struct IncludeStackInfo { - enum CurLexerKind CurLexerKind; + LexerCallback CurLexerCallback; Module *TheSubmodule; std::unique_ptr<Lexer> TheLexer; PreprocessorLexer *ThePPLexer; std::unique_ptr<TokenLexer> TheTokenLexer; - const DirectoryLookup *TheDirLookup; + ConstSearchDirIterator TheDirLookup; // The following constructors are completely useless copies of the default // versions, only needed to pacify MSVC. - IncludeStackInfo(enum CurLexerKind CurLexerKind, Module *TheSubmodule, + IncludeStackInfo(LexerCallback CurLexerCallback, Module *TheSubmodule, std::unique_ptr<Lexer> &&TheLexer, PreprocessorLexer *ThePPLexer, std::unique_ptr<TokenLexer> &&TheTokenLexer, - const DirectoryLookup *TheDirLookup) - : CurLexerKind(std::move(CurLexerKind)), + ConstSearchDirIterator TheDirLookup) + : CurLexerCallback(std::move(CurLexerCallback)), TheSubmodule(std::move(TheSubmodule)), TheLexer(std::move(TheLexer)), ThePPLexer(std::move(ThePPLexer)), TheTokenLexer(std::move(TheTokenLexer)), @@ -665,7 +890,7 @@ private: getActiveModuleMacros(Preprocessor &PP, const IdentifierInfo *II) const { if (auto *Info = getModuleInfo(PP, II)) return Info->ActiveModuleMacros; - return None; + return std::nullopt; } MacroDirective::DefInfo findDirectiveAtLoc(SourceLocation Loc, @@ -689,7 +914,7 @@ private: ArrayRef<ModuleMacro*> getOverriddenMacros() const { if (auto *Info = State.dyn_cast<ModuleMacroInfo*>()) return Info->OverriddenMacros; - return None; + return std::nullopt; } void setOverriddenMacros(Preprocessor &PP, @@ -764,6 +989,13 @@ private: /// in a submodule. SubmoduleState *CurSubmoduleState; + /// The files that have been included. + IncludedFilesSet IncludedFiles; + + /// The set of top-level modules that affected preprocessing, but were not + /// imported. + llvm::SmallSetVector<Module *, 2> AffectingClangModules; + /// The set of known macros exported from modules. llvm::FoldingSet<ModuleMacro> ModuleMacros; @@ -786,6 +1018,42 @@ private: using WarnUnusedMacroLocsTy = llvm::SmallDenseSet<SourceLocation, 32>; WarnUnusedMacroLocsTy WarnUnusedMacroLocs; + /// This is a pair of an optional message and source location used for pragmas + /// that annotate macros like pragma clang restrict_expansion and pragma clang + /// deprecated. This pair stores the optional message and the location of the + /// annotation pragma for use producing diagnostics and notes. + using MsgLocationPair = std::pair<std::string, SourceLocation>; + + struct MacroAnnotationInfo { + SourceLocation Location; + std::string Message; + }; + + struct MacroAnnotations { + std::optional<MacroAnnotationInfo> DeprecationInfo; + std::optional<MacroAnnotationInfo> RestrictExpansionInfo; + std::optional<SourceLocation> FinalAnnotationLoc; + + static MacroAnnotations makeDeprecation(SourceLocation Loc, + std::string Msg) { + return MacroAnnotations{MacroAnnotationInfo{Loc, std::move(Msg)}, + std::nullopt, std::nullopt}; + } + + static MacroAnnotations makeRestrictExpansion(SourceLocation Loc, + std::string Msg) { + return MacroAnnotations{ + std::nullopt, MacroAnnotationInfo{Loc, std::move(Msg)}, std::nullopt}; + } + + static MacroAnnotations makeFinal(SourceLocation Loc) { + return MacroAnnotations{std::nullopt, std::nullopt, Loc}; + } + }; + + /// Warning information for macro annotations. + llvm::DenseMap<const IdentifierInfo *, MacroAnnotations> AnnotationInfos; + /// A "freelist" of MacroArg objects that can be /// reused for quick allocation. MacroArgs *MacroArgCache = nullptr; @@ -872,21 +1140,25 @@ private: /// invoked (at which point the last position is popped). std::vector<CachedTokensTy::size_type> BacktrackPositions; - struct MacroInfoChain { - MacroInfo MI; - MacroInfoChain *Next; - }; + /// True if \p Preprocessor::SkipExcludedConditionalBlock() is running. + /// This is used to guard against calling this function recursively. + /// + /// See comments at the use-site for more context about why it is needed. + bool SkippingExcludedConditionalBlock = false; - /// MacroInfos are managed as a chain for easy disposal. This is the head - /// of that list. - MacroInfoChain *MIChainHead = nullptr; + /// Keeps track of skipped range mappings that were recorded while skipping + /// excluded conditional directives. It maps the source buffer pointer at + /// the beginning of a skipped block, to the number of bytes that should be + /// skipped. + llvm::DenseMap<const char *, unsigned> RecordedSkippedRanges; void updateOutOfDateIdentifier(IdentifierInfo &II) const; public: Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts, - DiagnosticsEngine &diags, LangOptions &opts, SourceManager &SM, - HeaderSearch &Headers, ModuleLoader &TheModuleLoader, + DiagnosticsEngine &diags, const LangOptions &LangOpts, + SourceManager &SM, HeaderSearch &Headers, + ModuleLoader &TheModuleLoader, IdentifierInfoLookup *IILookup = nullptr, bool OwnsHeaderSearch = false, TranslationUnitKind TUKind = TU_Complete); @@ -1161,7 +1433,7 @@ public: auto I = LeafModuleMacros.find(II); if (I != LeafModuleMacros.end()) return I->second; - return None; + return std::nullopt; } /// Get the list of submodules that we're currently building. @@ -1187,19 +1459,55 @@ public: /// \} + /// Mark the given clang module as affecting the current clang module or translation unit. + void markClangModuleAsAffecting(Module *M) { + assert(M->isModuleMapModule()); + if (!BuildingSubmoduleStack.empty()) { + if (M != BuildingSubmoduleStack.back().M) + BuildingSubmoduleStack.back().M->AffectingClangModules.insert(M); + } else { + AffectingClangModules.insert(M); + } + } + + /// Get the set of top-level clang modules that affected preprocessing, but were not + /// imported. + const llvm::SmallSetVector<Module *, 2> &getAffectingClangModules() const { + return AffectingClangModules; + } + + /// Mark the file as included. + /// Returns true if this is the first time the file was included. + bool markIncluded(FileEntryRef File) { + HeaderInfo.getFileInfo(File); + return IncludedFiles.insert(File).second; + } + + /// Return true if this header has already been included. + bool alreadyIncluded(FileEntryRef File) const { + HeaderInfo.getFileInfo(File); + return IncludedFiles.count(File); + } + + /// Get the set of included files. + IncludedFilesSet &getIncludedFiles() { return IncludedFiles; } + const IncludedFilesSet &getIncludedFiles() const { return IncludedFiles; } + /// Return the name of the macro defined before \p Loc that has /// spelling \p Tokens. If there are multiple macros with same spelling, /// return the last one defined. StringRef getLastMacroWithSpelling(SourceLocation Loc, ArrayRef<TokenValue> Tokens) const; + /// Get the predefines for this processor. + /// Used by some third-party tools to inspect and add predefines (see + /// https://github.com/llvm/llvm-project/issues/57483). const std::string &getPredefines() const { return Predefines; } /// Set the predefines for this Preprocessor. /// /// These predefines are automatically injected when parsing the main file. - void setPredefines(const char *P) { Predefines = P; } - void setPredefines(StringRef P) { Predefines = std::string(P); } + void setPredefines(std::string P) { Predefines = std::move(P); } /// Return information about the specified preprocessor /// identifier token. @@ -1330,8 +1638,8 @@ public: /// start lexing tokens from it instead of the current buffer. /// /// Emits a diagnostic, doesn't enter the file, and returns true on error. - bool EnterSourceFile(FileID FID, const DirectoryLookup *Dir, - SourceLocation Loc); + bool EnterSourceFile(FileID FID, ConstSearchDirIterator Dir, + SourceLocation Loc, bool IsFirstIncludeOfFile = true); /// Add a Macro to the top of the include stack and start lexing /// tokens from it instead of the current buffer. @@ -1409,6 +1717,9 @@ public: /// Lex the next token for this preprocessor. void Lex(Token &Result); + /// Lex all tokens for this preprocessor until (and excluding) end of file. + void LexTokensUntilEOF(std::vector<Token> *Tokens = nullptr); + /// Lex a token, forming a header-name token if possible. bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true); @@ -1585,7 +1896,7 @@ public: /// Determine whether it's possible for a future call to Lex to produce an /// annotation token created by a previous call to EnterAnnotationToken. bool mightHavePendingAnnotationTokens() { - return CurLexerKind != CLK_Lexer; + return CurLexerCallback != CLK_Lexer; } /// Update the current token to represent the provided @@ -1622,8 +1933,8 @@ public: /// (1-based). /// /// \returns true if an error occurred, false otherwise. - bool SetCodeCompletionPoint(const FileEntry *File, - unsigned Line, unsigned Column); + bool SetCodeCompletionPoint(FileEntryRef File, unsigned Line, + unsigned Column); /// Determine if we are performing code completion. bool isCodeCompletionEnabled() const { return CodeCompletionFile != nullptr; } @@ -1684,11 +1995,24 @@ public: PragmaAssumeNonNullLoc = Loc; } + /// Get the location of the recorded unterminated \#pragma clang + /// assume_nonnull begin in the preamble, if one exists. + /// + /// Returns an invalid location if the premable did not end with + /// such a pragma active or if there is no recorded preamble. + SourceLocation getPreambleRecordedPragmaAssumeNonNullLoc() const { + return PreambleRecordedPragmaAssumeNonNullLoc; + } + + /// Record the location of the unterminated \#pragma clang + /// assume_nonnull begin in the preamble. + void setPreambleRecordedPragmaAssumeNonNullLoc(SourceLocation Loc) { + PreambleRecordedPragmaAssumeNonNullLoc = Loc; + } + /// Set the directory in which the main file should be considered /// to have been found, if it is not a real file. - void setMainFileDir(const DirectoryEntry *Dir) { - MainFileDir = Dir; - } + void setMainFileDir(DirectoryEntryRef Dir) { MainFileDir = Dir; } /// Instruct the preprocessor to skip part of the main source file. /// @@ -1953,8 +2277,7 @@ public: /// This either returns the EOF token and returns true, or /// pops a level off the include stack and returns false, at which point the /// client should call lex again. - bool HandleEndOfFile(Token &Result, SourceLocation Loc, - bool isEndOfMacro = false); + bool HandleEndOfFile(Token &Result, bool isEndOfMacro = false); /// Callback invoked when the current TokenLexer hits the end of its /// token stream. @@ -1990,9 +2313,74 @@ public: unsigned getCounterValue() const { return CounterValue; } void setCounterValue(unsigned V) { CounterValue = V; } + LangOptions::FPEvalMethodKind getCurrentFPEvalMethod() const { + assert(CurrentFPEvalMethod != LangOptions::FEM_UnsetOnCommandLine && + "FPEvalMethod should be set either from command line or from the " + "target info"); + return CurrentFPEvalMethod; + } + + LangOptions::FPEvalMethodKind getTUFPEvalMethod() const { + return TUFPEvalMethod; + } + + SourceLocation getLastFPEvalPragmaLocation() const { + return LastFPEvalPragmaLocation; + } + + void setCurrentFPEvalMethod(SourceLocation PragmaLoc, + LangOptions::FPEvalMethodKind Val) { + assert(Val != LangOptions::FEM_UnsetOnCommandLine && + "FPEvalMethod should never be set to FEM_UnsetOnCommandLine"); + // This is the location of the '#pragma float_control" where the + // execution state is modifed. + LastFPEvalPragmaLocation = PragmaLoc; + CurrentFPEvalMethod = Val; + TUFPEvalMethod = Val; + } + + void setTUFPEvalMethod(LangOptions::FPEvalMethodKind Val) { + assert(Val != LangOptions::FEM_UnsetOnCommandLine && + "TUPEvalMethod should never be set to FEM_UnsetOnCommandLine"); + TUFPEvalMethod = Val; + } + /// Retrieves the module that we're currently building, if any. Module *getCurrentModule(); + /// Retrieves the module whose implementation we're current compiling, if any. + Module *getCurrentModuleImplementation(); + + /// If we are preprocessing a named module. + bool isInNamedModule() const { return ModuleDeclState.isNamedModule(); } + + /// If we are proprocessing a named interface unit. + /// Note that a module implementation partition is not considered as an + /// named interface unit here although it is importable + /// to ease the parsing. + bool isInNamedInterfaceUnit() const { + return ModuleDeclState.isNamedInterface(); + } + + /// Get the named module name we're preprocessing. + /// Requires we're preprocessing a named module. + StringRef getNamedModuleName() const { return ModuleDeclState.getName(); } + + /// If we are implementing an implementation module unit. + /// Note that the module implementation partition is not considered as an + /// implementation unit. + bool isInImplementationUnit() const { + return ModuleDeclState.isImplementationUnit(); + } + + /// If we're importing a standard C++20 Named Modules. + bool isInImportingCXXNamedModules() const { + // NamedModuleImportPath will be non-empty only if we're importing + // Standard C++ named modules. + return !NamedModuleImportPath.empty() && getLangOpts().CPlusPlusModules && + !IsAtImport; + } + /// Allocate a new MacroInfo object with the provided SourceLocation. MacroInfo *AllocateMacroInfo(SourceLocation L); @@ -2009,22 +2397,16 @@ public: /// Given a "foo" or \<foo> reference, look up the indicated file. /// - /// Returns None on failure. \p isAngled indicates whether the file + /// Returns std::nullopt on failure. \p isAngled indicates whether the file /// reference is for system \#include's or not (i.e. using <> instead of ""). - Optional<FileEntryRef> + OptionalFileEntryRef LookupFile(SourceLocation FilenameLoc, StringRef Filename, bool isAngled, - const DirectoryLookup *FromDir, const FileEntry *FromFile, - const DirectoryLookup *&CurDir, SmallVectorImpl<char> *SearchPath, + ConstSearchDirIterator FromDir, const FileEntry *FromFile, + ConstSearchDirIterator *CurDir, SmallVectorImpl<char> *SearchPath, SmallVectorImpl<char> *RelativePath, ModuleMap::KnownHeader *SuggestedModule, bool *IsMapped, - bool *IsFrameworkFound, bool SkipCache = false); - - /// Get the DirectoryLookup structure used to find the current - /// FileEntry, if CurLexer is non-null and if applicable. - /// - /// This allows us to implement \#include_next and find directory-specific - /// properties. - const DirectoryLookup *GetCurDirLookup() { return CurDirLookup; } + bool *IsFrameworkFound, bool SkipCache = false, + bool OpenFile = true, bool CacheFailures = true); /// Return true if we're in the top-level file, not in a \#include. bool isInPrimaryFile() const; @@ -2043,8 +2425,9 @@ private: friend void TokenLexer::ExpandFunctionArguments(); void PushIncludeMacroStack() { - assert(CurLexerKind != CLK_CachingLexer && "cannot push a caching lexer"); - IncludeMacroStack.emplace_back(CurLexerKind, CurLexerSubmodule, + assert(CurLexerCallback != CLK_CachingLexer && + "cannot push a caching lexer"); + IncludeMacroStack.emplace_back(CurLexerCallback, CurLexerSubmodule, std::move(CurLexer), CurPPLexer, std::move(CurTokenLexer), CurDirLookup); CurPPLexer = nullptr; @@ -2056,7 +2439,7 @@ private: CurTokenLexer = std::move(IncludeMacroStack.back().TheTokenLexer); CurDirLookup = IncludeMacroStack.back().TheDirLookup; CurLexerSubmodule = IncludeMacroStack.back().TheSubmodule; - CurLexerKind = IncludeMacroStack.back().CurLexerKind; + CurLexerCallback = IncludeMacroStack.back().CurLexerCallback; IncludeMacroStack.pop_back(); } @@ -2107,6 +2490,13 @@ private: /// Return true if an error occurs parsing the arg list. bool ReadMacroParameterList(MacroInfo *MI, Token& LastTok); + /// Provide a suggestion for a typoed directive. If there is no typo, then + /// just skip suggesting. + /// + /// \param Tok - Token that represents the directive + /// \param Directive - String reference for the directive name + void SuggestTypoedDirective(const Token &Tok, StringRef Directive) const; + /// We just read a \#if or related directive and decided that the /// subsequent tokens are in the \#if'd out portion of the /// file. Lex the rest of the file, until we see an \#endif. If \p @@ -2139,6 +2529,20 @@ private: /// If the expression is equivalent to "!defined(X)" return X in IfNDefMacro. DirectiveEvalResult EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro); + /// Process a '__has_include("path")' expression. + /// + /// Returns true if successful. + bool EvaluateHasInclude(Token &Tok, IdentifierInfo *II); + + /// Process '__has_include_next("path")' expression. + /// + /// Returns true if successful. + bool EvaluateHasIncludeNext(Token &Tok, IdentifierInfo *II); + + /// Get the directory and file from which to start \#include_next lookup. + std::pair<ConstSearchDirIterator, const FileEntry *> + getIncludeNextStart(const Token &IncludeNextTok) const; + /// Install the standard preprocessor pragmas: /// \#pragma GCC poison/system_header/dependency and \#pragma once. void RegisterBuiltinPragmas(); @@ -2186,7 +2590,7 @@ private: /// Add a lexer to the top of the include stack and /// start lexing tokens from it instead of the current buffer. - void EnterSourceFileWithLexer(Lexer *TheLexer, const DirectoryLookup *Dir); + void EnterSourceFileWithLexer(Lexer *TheLexer, ConstSearchDirIterator Dir); /// Set the FileID for the preprocessor predefines. void setPredefinesFileID(FileID FID) { @@ -2250,6 +2654,7 @@ private: None, ModuleBegin, ModuleImport, + HeaderUnitImport, SkippedModuleImport, Failure, } Kind; @@ -2262,23 +2667,23 @@ private: } }; - Optional<FileEntryRef> LookupHeaderIncludeOrImport( - const DirectoryLookup *&CurDir, StringRef &Filename, + OptionalFileEntryRef LookupHeaderIncludeOrImport( + ConstSearchDirIterator *CurDir, StringRef &Filename, SourceLocation FilenameLoc, CharSourceRange FilenameRange, const Token &FilenameTok, bool &IsFrameworkFound, bool IsImportDecl, - bool &IsMapped, const DirectoryLookup *LookupFrom, + bool &IsMapped, ConstSearchDirIterator LookupFrom, const FileEntry *LookupFromFile, StringRef &LookupFilename, SmallVectorImpl<char> &RelativePath, SmallVectorImpl<char> &SearchPath, ModuleMap::KnownHeader &SuggestedModule, bool isAngled); // File inclusion. void HandleIncludeDirective(SourceLocation HashLoc, Token &Tok, - const DirectoryLookup *LookupFrom = nullptr, + ConstSearchDirIterator LookupFrom = nullptr, const FileEntry *LookupFromFile = nullptr); ImportAction HandleHeaderIncludeOrImport(SourceLocation HashLoc, Token &IncludeTok, Token &FilenameTok, SourceLocation EndLoc, - const DirectoryLookup *LookupFrom = nullptr, + ConstSearchDirIterator LookupFrom = nullptr, const FileEntry *LookupFromFile = nullptr); void HandleIncludeNextDirective(SourceLocation HashLoc, Token &Tok); void HandleIncludeMacrosDirective(SourceLocation HashLoc, Token &Tok); @@ -2291,13 +2696,13 @@ public: /// \c false if the module appears to be usable. static bool checkModuleIsAvailable(const LangOptions &LangOpts, const TargetInfo &TargetInfo, - DiagnosticsEngine &Diags, Module *M); + const Module &M, DiagnosticsEngine &Diags); // Module inclusion testing. /// Find the module that owns the source or header file that /// \p Loc points to. If the location is in a file that was included /// into a module, or is outside any module, returns nullptr. - Module *getModuleForLocation(SourceLocation Loc); + Module *getModuleForLocation(SourceLocation Loc, bool AllowTextual); /// We want to produce a diagnostic at location IncLoc concerning an /// unreachable effect at location MLoc (eg, where a desired entity was @@ -2313,8 +2718,8 @@ public: /// \return A file that can be #included to provide the desired effect. Null /// if no such file could be determined or if a #include is not /// appropriate (eg, if a module should be imported instead). - const FileEntry *getHeaderToIncludeForDiagnostics(SourceLocation IncLoc, - SourceLocation MLoc); + OptionalFileEntryRef getHeaderToIncludeForDiagnostics(SourceLocation IncLoc, + SourceLocation MLoc); bool isRecordingPreamble() const { return PreambleConditionalStack.isRecording(); @@ -2332,14 +2737,14 @@ public: PreambleConditionalStack.setStack(s); } - void setReplayablePreambleConditionalStack(ArrayRef<PPConditionalInfo> s, - llvm::Optional<PreambleSkipInfo> SkipInfo) { + void setReplayablePreambleConditionalStack( + ArrayRef<PPConditionalInfo> s, std::optional<PreambleSkipInfo> SkipInfo) { PreambleConditionalStack.startReplaying(); PreambleConditionalStack.setStack(s); PreambleConditionalStack.SkipInfo = SkipInfo; } - llvm::Optional<PreambleSkipInfo> getPreambleSkipInfo() const { + std::optional<PreambleSkipInfo> getPreambleSkipInfo() const { return PreambleConditionalStack.SkipInfo; } @@ -2364,14 +2769,12 @@ private: // Pragmas. void HandlePragmaDirective(PragmaIntroducer Introducer); - void ResolvePragmaIncludeInstead(SourceLocation Location) const; public: void HandlePragmaOnce(Token &OnceTok); void HandlePragmaMark(Token &MarkTok); void HandlePragmaPoison(); void HandlePragmaSystemHeader(Token &SysHeaderTok); - void HandlePragmaIncludeInstead(Token &Tok); void HandlePragmaDependency(Token &DependencyTok); void HandlePragmaPushMacro(Token &Tok); void HandlePragmaPopMacro(Token &Tok); @@ -2388,14 +2791,141 @@ public: /// warnings. void markMacroAsUsed(MacroInfo *MI); + void addMacroDeprecationMsg(const IdentifierInfo *II, std::string Msg, + SourceLocation AnnotationLoc) { + auto Annotations = AnnotationInfos.find(II); + if (Annotations == AnnotationInfos.end()) + AnnotationInfos.insert(std::make_pair( + II, + MacroAnnotations::makeDeprecation(AnnotationLoc, std::move(Msg)))); + else + Annotations->second.DeprecationInfo = + MacroAnnotationInfo{AnnotationLoc, std::move(Msg)}; + } + + void addRestrictExpansionMsg(const IdentifierInfo *II, std::string Msg, + SourceLocation AnnotationLoc) { + auto Annotations = AnnotationInfos.find(II); + if (Annotations == AnnotationInfos.end()) + AnnotationInfos.insert( + std::make_pair(II, MacroAnnotations::makeRestrictExpansion( + AnnotationLoc, std::move(Msg)))); + else + Annotations->second.RestrictExpansionInfo = + MacroAnnotationInfo{AnnotationLoc, std::move(Msg)}; + } + + void addFinalLoc(const IdentifierInfo *II, SourceLocation AnnotationLoc) { + auto Annotations = AnnotationInfos.find(II); + if (Annotations == AnnotationInfos.end()) + AnnotationInfos.insert( + std::make_pair(II, MacroAnnotations::makeFinal(AnnotationLoc))); + else + Annotations->second.FinalAnnotationLoc = AnnotationLoc; + } + + const MacroAnnotations &getMacroAnnotations(const IdentifierInfo *II) const { + return AnnotationInfos.find(II)->second; + } + + void emitMacroExpansionWarnings(const Token &Identifier, + bool IsIfnDef = false) const { + IdentifierInfo *Info = Identifier.getIdentifierInfo(); + if (Info->isDeprecatedMacro()) + emitMacroDeprecationWarning(Identifier); + + if (Info->isRestrictExpansion() && + !SourceMgr.isInMainFile(Identifier.getLocation())) + emitRestrictExpansionWarning(Identifier); + + if (!IsIfnDef) { + if (Info->getName() == "INFINITY" && getLangOpts().NoHonorInfs) + emitRestrictInfNaNWarning(Identifier, 0); + if (Info->getName() == "NAN" && getLangOpts().NoHonorNaNs) + emitRestrictInfNaNWarning(Identifier, 1); + } + } + + static void processPathForFileMacro(SmallVectorImpl<char> &Path, + const LangOptions &LangOpts, + const TargetInfo &TI); + + static void processPathToFileName(SmallVectorImpl<char> &FileName, + const PresumedLoc &PLoc, + const LangOptions &LangOpts, + const TargetInfo &TI); + private: - Optional<unsigned> - getSkippedRangeForExcludedConditionalBlock(SourceLocation HashLoc); + void emitMacroDeprecationWarning(const Token &Identifier) const; + void emitRestrictExpansionWarning(const Token &Identifier) const; + void emitFinalMacroWarning(const Token &Identifier, bool IsUndef) const; + void emitRestrictInfNaNWarning(const Token &Identifier, + unsigned DiagSelection) const; + + /// This boolean state keeps track if the current scanned token (by this PP) + /// is in an "-Wunsafe-buffer-usage" opt-out region. Assuming PP scans a + /// translation unit in a linear order. + bool InSafeBufferOptOutRegion = false; + + /// Hold the start location of the current "-Wunsafe-buffer-usage" opt-out + /// region if PP is currently in such a region. Hold undefined value + /// otherwise. + SourceLocation CurrentSafeBufferOptOutStart; // It is used to report the start location of an never-closed region. + + // An ordered sequence of "-Wunsafe-buffer-usage" opt-out regions in one + // translation unit. Each region is represented by a pair of start and end + // locations. A region is "open" if its' start and end locations are + // identical. + SmallVector<std::pair<SourceLocation, SourceLocation>, 8> SafeBufferOptOutMap; - /// Contains the currently active skipped range mappings for skipping excluded - /// conditional directives. - ExcludedPreprocessorDirectiveSkipMapping - *ExcludedConditionalDirectiveSkipMappings; +public: + /// \return true iff the given `Loc` is in a "-Wunsafe-buffer-usage" opt-out + /// region. This `Loc` must be a source location that has been pre-processed. + bool isSafeBufferOptOut(const SourceManager&SourceMgr, const SourceLocation &Loc) const; + + /// Alter the state of whether this PP currently is in a + /// "-Wunsafe-buffer-usage" opt-out region. + /// + /// \param isEnter true if this PP is entering a region; otherwise, this PP + /// is exiting a region + /// \param Loc the location of the entry or exit of a + /// region + /// \return true iff it is INVALID to enter or exit a region, i.e., + /// attempt to enter a region before exiting a previous region, or exiting a + /// region that PP is not currently in. + bool enterOrExitSafeBufferOptOutRegion(bool isEnter, + const SourceLocation &Loc); + + /// \return true iff this PP is currently in a "-Wunsafe-buffer-usage" + /// opt-out region + bool isPPInSafeBufferOptOutRegion(); + + /// \param StartLoc output argument. It will be set to the start location of + /// the current "-Wunsafe-buffer-usage" opt-out region iff this function + /// returns true. + /// \return true iff this PP is currently in a "-Wunsafe-buffer-usage" + /// opt-out region + bool isPPInSafeBufferOptOutRegion(SourceLocation &StartLoc); + +private: + /// Helper functions to forward lexing to the actual lexer. They all share the + /// same signature. + static bool CLK_Lexer(Preprocessor &P, Token &Result) { + return P.CurLexer->Lex(Result); + } + static bool CLK_TokenLexer(Preprocessor &P, Token &Result) { + return P.CurTokenLexer->Lex(Result); + } + static bool CLK_CachingLexer(Preprocessor &P, Token &Result) { + P.CachingLex(Result); + return true; + } + static bool CLK_DependencyDirectivesLexer(Preprocessor &P, Token &Result) { + return P.CurLexer->LexDependencyDirectiveToken(Result); + } + static bool CLK_LexAfterModuleImport(Preprocessor &P, Token &Result) { + return P.LexAfterModuleImport(Result); + } }; /// Abstract base class that describes a handler that will receive |