1 //===--- NoTrivialPPDirectiveTracer.h ---------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the NoTrivialPPDirectiveTracer interface. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H 14 #define LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H 15 16 #include "clang/Lex/PPCallbacks.h" 17 18 namespace clang { 19 class Preprocessor; 20 21 /// Consider the following code: 22 /// 23 /// # 1 __FILE__ 1 3 24 /// export module a; 25 /// 26 /// According to the wording in 27 /// [P1857R3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1857r3.html): 28 /// 29 /// A module directive may only appear as the first preprocessing tokens in a 30 /// file (excluding the global module fragment.) 31 /// 32 /// and the wording in 33 /// [[cpp.pre]](https://eel.is/c++draft/cpp.pre#nt:module-file): 34 /// module-file: 35 /// pp-global-module-fragment[opt] pp-module group[opt] 36 /// pp-private-module-fragment[opt] 37 /// 38 /// `#` is the first pp-token in the translation unit, and it was rejected by 39 /// clang, but they really should be exempted from this rule. The goal is to not 40 /// allow any preprocessor conditionals or most state changes, but these don't 41 /// fit that. 42 /// 43 /// State change would mean most semantically observable preprocessor state, 44 /// particularly anything that is order dependent. Global flags like being a 45 /// system header/module shouldn't matter. 46 /// 47 /// We should exempt a brunch of directives, even though it violates the current 48 /// standard wording. 49 /// 50 /// This class used to trace 'no-trivial' pp-directives in main file, which may 51 /// change the preprocessing state. 52 /// 53 /// FIXME: Once the wording of the standard is revised, we need to follow the 54 /// wording of the standard. Currently this is just a workaround 55 class NoTrivialPPDirectiveTracer : public PPCallbacks { 56 Preprocessor &PP; 57 58 /// Whether preprocessing main file. We only focus on the main file. 59 bool InMainFile = true; 60 61 /// Whether one or more conditional, include or other 'no-trivial' 62 /// pp-directives has seen before. 63 bool SeenNoTrivialPPDirective = false; 64 65 void setSeenNoTrivialPPDirective(); 66 67 public: NoTrivialPPDirectiveTracer(Preprocessor & P)68 NoTrivialPPDirectiveTracer(Preprocessor &P) : PP(P) {} 69 70 bool hasSeenNoTrivialPPDirective() const; 71 72 /// Callback invoked whenever the \p Lexer moves to a different file for 73 /// lexing. Unlike \p FileChanged line number directives and other related 74 /// pragmas do not trigger callbacks to \p LexedFileChanged. 75 /// 76 /// \param FID The \p FileID that the \p Lexer moved to. 77 /// 78 /// \param Reason Whether the \p Lexer entered a new file or exited one. 79 /// 80 /// \param FileType The \p CharacteristicKind of the file the \p Lexer moved 81 /// to. 82 /// 83 /// \param PrevFID The \p FileID the \p Lexer was using before the change. 84 /// 85 /// \param Loc The location where the \p Lexer entered a new file from or the 86 /// location that the \p Lexer moved into after exiting a file. 87 void LexedFileChanged(FileID FID, LexedFileChangeReason Reason, 88 SrcMgr::CharacteristicKind FileType, FileID PrevFID, 89 SourceLocation Loc) override; 90 91 /// Callback invoked whenever an embed directive has been processed, 92 /// regardless of whether the embed will actually find a file. 93 /// 94 /// \param HashLoc The location of the '#' that starts the embed directive. 95 /// 96 /// \param FileName The name of the file being included, as written in the 97 /// source code. 98 /// 99 /// \param IsAngled Whether the file name was enclosed in angle brackets; 100 /// otherwise, it was enclosed in quotes. 101 /// 102 /// \param File The actual file that may be included by this embed directive. 103 /// 104 /// \param Params The parameters used by the directive. EmbedDirective(SourceLocation HashLoc,StringRef FileName,bool IsAngled,OptionalFileEntryRef File,const LexEmbedParametersResult & Params)105 void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled, 106 OptionalFileEntryRef File, 107 const LexEmbedParametersResult &Params) override { 108 setSeenNoTrivialPPDirective(); 109 } 110 111 /// Callback invoked whenever an inclusion directive of 112 /// any kind (\c \#include, \c \#import, etc.) has been processed, regardless 113 /// of whether the inclusion will actually result in an inclusion. 114 /// 115 /// \param HashLoc The location of the '#' that starts the inclusion 116 /// directive. 117 /// 118 /// \param IncludeTok The token that indicates the kind of inclusion 119 /// directive, e.g., 'include' or 'import'. 120 /// 121 /// \param FileName The name of the file being included, as written in the 122 /// source code. 123 /// 124 /// \param IsAngled Whether the file name was enclosed in angle brackets; 125 /// otherwise, it was enclosed in quotes. 126 /// 127 /// \param FilenameRange The character range of the quotes or angle brackets 128 /// for the written file name. 129 /// 130 /// \param File The actual file that may be included by this inclusion 131 /// directive. 132 /// 133 /// \param SearchPath Contains the search path which was used to find the file 134 /// in the file system. If the file was found via an absolute include path, 135 /// SearchPath will be empty. For framework includes, the SearchPath and 136 /// RelativePath will be split up. For example, if an include of "Some/Some.h" 137 /// is found via the framework path 138 /// "path/to/Frameworks/Some.framework/Headers/Some.h", SearchPath will be 139 /// "path/to/Frameworks/Some.framework/Headers" and RelativePath will be 140 /// "Some.h". 141 /// 142 /// \param RelativePath The path relative to SearchPath, at which the include 143 /// file was found. This is equal to FileName except for framework includes. 144 /// 145 /// \param SuggestedModule The module suggested for this header, if any. 146 /// 147 /// \param ModuleImported Whether this include was translated into import of 148 /// \p SuggestedModule. 149 /// 150 /// \param FileType The characteristic kind, indicates whether a file or 151 /// directory holds normal user code, system code, or system code which is 152 /// implicitly 'extern "C"' in C++ mode. 153 /// 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)154 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, 155 StringRef FileName, bool IsAngled, 156 CharSourceRange FilenameRange, 157 OptionalFileEntryRef File, StringRef SearchPath, 158 StringRef RelativePath, const Module *SuggestedModule, 159 bool ModuleImported, 160 SrcMgr::CharacteristicKind FileType) override { 161 setSeenNoTrivialPPDirective(); 162 } 163 164 /// Callback invoked whenever there was an explicit module-import 165 /// syntax. 166 /// 167 /// \param ImportLoc The location of import directive token. 168 /// 169 /// \param Path The identifiers (and their locations) of the module 170 /// "path", e.g., "std.vector" would be split into "std" and "vector". 171 /// 172 /// \param Imported The imported module; can be null if importing failed. 173 /// moduleImport(SourceLocation ImportLoc,ModuleIdPath Path,const Module * Imported)174 void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path, 175 const Module *Imported) override { 176 setSeenNoTrivialPPDirective(); 177 } 178 179 /// Callback invoked when the end of the main file is reached. 180 /// 181 /// No subsequent callbacks will be made. EndOfMainFile()182 void EndOfMainFile() override { setSeenNoTrivialPPDirective(); } 183 184 /// Callback invoked when start reading any pragma directive. PragmaDirective(SourceLocation Loc,PragmaIntroducerKind Introducer)185 void PragmaDirective(SourceLocation Loc, 186 PragmaIntroducerKind Introducer) override {} 187 188 /// Called by Preprocessor::HandleMacroExpandedIdentifier when a 189 /// macro invocation is found. 190 void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, 191 SourceRange Range, const MacroArgs *Args) override; 192 193 /// Hook called whenever a macro definition is seen. MacroDefined(const Token & MacroNameTok,const MacroDirective * MD)194 void MacroDefined(const Token &MacroNameTok, 195 const MacroDirective *MD) override { 196 setSeenNoTrivialPPDirective(); 197 } 198 199 /// Hook called whenever a macro \#undef is seen. 200 /// \param MacroNameTok The active Token 201 /// \param MD A MacroDefinition for the named macro. 202 /// \param Undef New MacroDirective if the macro was defined, null otherwise. 203 /// 204 /// MD is released immediately following this callback. MacroUndefined(const Token & MacroNameTok,const MacroDefinition & MD,const MacroDirective * Undef)205 void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, 206 const MacroDirective *Undef) override { 207 setSeenNoTrivialPPDirective(); 208 } 209 210 /// Hook called whenever the 'defined' operator is seen. 211 /// \param MD The MacroDirective if the name was a macro, null otherwise. Defined(const Token & MacroNameTok,const MacroDefinition & MD,SourceRange Range)212 void Defined(const Token &MacroNameTok, const MacroDefinition &MD, 213 SourceRange Range) override { 214 setSeenNoTrivialPPDirective(); 215 } 216 217 /// Hook called whenever an \#if is seen. 218 /// \param Loc the source location of the directive. 219 /// \param ConditionRange The SourceRange of the expression being tested. 220 /// \param ConditionValue The evaluated value of the condition. 221 /// 222 // FIXME: better to pass in a list (or tree!) of Tokens. If(SourceLocation Loc,SourceRange ConditionRange,ConditionValueKind ConditionValue)223 void If(SourceLocation Loc, SourceRange ConditionRange, 224 ConditionValueKind ConditionValue) override { 225 setSeenNoTrivialPPDirective(); 226 } 227 228 /// Hook called whenever an \#elif is seen. 229 /// \param Loc the source location of the directive. 230 /// \param ConditionRange The SourceRange of the expression being tested. 231 /// \param ConditionValue The evaluated value of the condition. 232 /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. 233 // FIXME: better to pass in a list (or tree!) of Tokens. Elif(SourceLocation Loc,SourceRange ConditionRange,ConditionValueKind ConditionValue,SourceLocation IfLoc)234 void Elif(SourceLocation Loc, SourceRange ConditionRange, 235 ConditionValueKind ConditionValue, SourceLocation IfLoc) override { 236 setSeenNoTrivialPPDirective(); 237 } 238 239 /// Hook called whenever an \#ifdef is seen. 240 /// \param Loc the source location of the directive. 241 /// \param MacroNameTok Information on the token being tested. 242 /// \param MD The MacroDefinition if the name was a macro, null otherwise. Ifdef(SourceLocation Loc,const Token & MacroNameTok,const MacroDefinition & MD)243 void Ifdef(SourceLocation Loc, const Token &MacroNameTok, 244 const MacroDefinition &MD) override { 245 setSeenNoTrivialPPDirective(); 246 } 247 248 /// Hook called whenever an \#elifdef branch is taken. 249 /// \param Loc the source location of the directive. 250 /// \param MacroNameTok Information on the token being tested. 251 /// \param MD The MacroDefinition if the name was a macro, null otherwise. Elifdef(SourceLocation Loc,const Token & MacroNameTok,const MacroDefinition & MD)252 void Elifdef(SourceLocation Loc, const Token &MacroNameTok, 253 const MacroDefinition &MD) override { 254 setSeenNoTrivialPPDirective(); 255 } 256 /// Hook called whenever an \#elifdef is skipped. 257 /// \param Loc the source location of the directive. 258 /// \param ConditionRange The SourceRange of the expression being tested. 259 /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. 260 // FIXME: better to pass in a list (or tree!) of Tokens. Elifdef(SourceLocation Loc,SourceRange ConditionRange,SourceLocation IfLoc)261 void Elifdef(SourceLocation Loc, SourceRange ConditionRange, 262 SourceLocation IfLoc) override { 263 setSeenNoTrivialPPDirective(); 264 } 265 266 /// Hook called whenever an \#ifndef is seen. 267 /// \param Loc the source location of the directive. 268 /// \param MacroNameTok Information on the token being tested. 269 /// \param MD The MacroDefiniton if the name was a macro, null otherwise. Ifndef(SourceLocation Loc,const Token & MacroNameTok,const MacroDefinition & MD)270 void Ifndef(SourceLocation Loc, const Token &MacroNameTok, 271 const MacroDefinition &MD) override { 272 setSeenNoTrivialPPDirective(); 273 } 274 275 /// Hook called whenever an \#elifndef branch is taken. 276 /// \param Loc the source location of the directive. 277 /// \param MacroNameTok Information on the token being tested. 278 /// \param MD The MacroDefinition if the name was a macro, null otherwise. Elifndef(SourceLocation Loc,const Token & MacroNameTok,const MacroDefinition & MD)279 void Elifndef(SourceLocation Loc, const Token &MacroNameTok, 280 const MacroDefinition &MD) override { 281 setSeenNoTrivialPPDirective(); 282 } 283 /// Hook called whenever an \#elifndef is skipped. 284 /// \param Loc the source location of the directive. 285 /// \param ConditionRange The SourceRange of the expression being tested. 286 /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. 287 // FIXME: better to pass in a list (or tree!) of Tokens. Elifndef(SourceLocation Loc,SourceRange ConditionRange,SourceLocation IfLoc)288 void Elifndef(SourceLocation Loc, SourceRange ConditionRange, 289 SourceLocation IfLoc) override { 290 setSeenNoTrivialPPDirective(); 291 } 292 293 /// Hook called whenever an \#else is seen. 294 /// \param Loc the source location of the directive. 295 /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. Else(SourceLocation Loc,SourceLocation IfLoc)296 void Else(SourceLocation Loc, SourceLocation IfLoc) override { 297 setSeenNoTrivialPPDirective(); 298 } 299 300 /// Hook called whenever an \#endif is seen. 301 /// \param Loc the source location of the directive. 302 /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive. Endif(SourceLocation Loc,SourceLocation IfLoc)303 void Endif(SourceLocation Loc, SourceLocation IfLoc) override { 304 setSeenNoTrivialPPDirective(); 305 } 306 }; 307 308 } // namespace clang 309 310 #endif // LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H 311