1 //===--- MultipleIncludeOpt.h - Header Multiple-Include Optzn ---*- 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 /// \file 10 /// Defines the MultipleIncludeOpt interface. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_LEX_MULTIPLEINCLUDEOPT_H 15 #define LLVM_CLANG_LEX_MULTIPLEINCLUDEOPT_H 16 17 #include "clang/Basic/SourceLocation.h" 18 19 namespace clang { 20 class IdentifierInfo; 21 22 /// Implements the simple state machine that the Lexer class uses to 23 /// detect files subject to the 'multiple-include' optimization. 24 /// 25 /// The public methods in this class are triggered by various 26 /// events that occur when a file is lexed, and after the entire file is lexed, 27 /// information about which macro (if any) controls the header is returned. 28 class MultipleIncludeOpt { 29 /// ReadAnyTokens - This is set to false when a file is first opened and true 30 /// any time a token is returned to the client or a (non-multiple-include) 31 /// directive is parsed. When the final \#endif is parsed this is reset back 32 /// to false, that way any tokens before the first \#ifdef or after the last 33 /// \#endif can be easily detected. 34 bool ReadAnyTokens; 35 36 /// ImmediatelyAfterTopLevelIfndef - This is true when the only tokens 37 /// processed in the file so far is an #ifndef and an identifier. Used in 38 /// the detection of header guards in a file. 39 bool ImmediatelyAfterTopLevelIfndef; 40 41 /// ReadAnyTokens - This is set to false when a file is first opened and true 42 /// any time a token is returned to the client or a (non-multiple-include) 43 /// directive is parsed. When the final #endif is parsed this is reset back 44 /// to false, that way any tokens before the first #ifdef or after the last 45 /// #endif can be easily detected. 46 bool DidMacroExpansion; 47 48 /// TheMacro - The controlling macro for a file, if valid. 49 /// 50 const IdentifierInfo *TheMacro; 51 52 /// DefinedMacro - The macro defined right after TheMacro, if any. 53 const IdentifierInfo *DefinedMacro; 54 55 SourceLocation MacroLoc; 56 SourceLocation DefinedLoc; 57 public: MultipleIncludeOpt()58 MultipleIncludeOpt() { 59 ReadAnyTokens = false; 60 ImmediatelyAfterTopLevelIfndef = false; 61 DidMacroExpansion = false; 62 TheMacro = nullptr; 63 DefinedMacro = nullptr; 64 } 65 GetMacroLocation()66 SourceLocation GetMacroLocation() const { 67 return MacroLoc; 68 } 69 GetDefinedLocation()70 SourceLocation GetDefinedLocation() const { 71 return DefinedLoc; 72 } 73 resetImmediatelyAfterTopLevelIfndef()74 void resetImmediatelyAfterTopLevelIfndef() { 75 ImmediatelyAfterTopLevelIfndef = false; 76 } 77 SetDefinedMacro(IdentifierInfo * M,SourceLocation Loc)78 void SetDefinedMacro(IdentifierInfo *M, SourceLocation Loc) { 79 DefinedMacro = M; 80 DefinedLoc = Loc; 81 } 82 83 /// Invalidate - Permanently mark this file as not being suitable for the 84 /// include-file optimization. Invalidate()85 void Invalidate() { 86 // If we have read tokens but have no controlling macro, the state-machine 87 // below can never "accept". 88 ReadAnyTokens = true; 89 ImmediatelyAfterTopLevelIfndef = false; 90 DefinedMacro = nullptr; 91 TheMacro = nullptr; 92 } 93 94 /// getHasReadAnyTokensVal - This is used for the \#ifndef handshake at the 95 /// top of the file when reading preprocessor directives. Otherwise, reading 96 /// the "ifndef x" would count as reading tokens. getHasReadAnyTokensVal()97 bool getHasReadAnyTokensVal() const { return ReadAnyTokens; } 98 99 /// getImmediatelyAfterTopLevelIfndef - returns true if the last directive 100 /// was an #ifndef at the beginning of the file. getImmediatelyAfterTopLevelIfndef()101 bool getImmediatelyAfterTopLevelIfndef() const { 102 return ImmediatelyAfterTopLevelIfndef; 103 } 104 105 // If a token is read, remember that we have seen a side-effect in this file. ReadToken()106 void ReadToken() { 107 ReadAnyTokens = true; 108 ImmediatelyAfterTopLevelIfndef = false; 109 } 110 111 /// SetReadToken - Set whether the value of 'ReadAnyTokens'. Called to 112 /// override when encountering tokens outside of the include guard that have 113 /// no effect if the file in question is is included multiple times (e.g. the 114 /// null directive). SetReadToken(bool Value)115 void SetReadToken(bool Value) { ReadAnyTokens = Value; } 116 117 /// ExpandedMacro - When a macro is expanded with this lexer as the current 118 /// buffer, this method is called to disable the MIOpt if needed. ExpandedMacro()119 void ExpandedMacro() { DidMacroExpansion = true; } 120 121 /// Called when entering a top-level \#ifndef directive (or the 122 /// "\#if !defined" equivalent) without any preceding tokens. 123 /// 124 /// Note, we don't care about the input value of 'ReadAnyTokens'. The caller 125 /// ensures that this is only called if there are no tokens read before the 126 /// \#ifndef. The caller is required to do this, because reading the \#if 127 /// line obviously reads in tokens. EnterTopLevelIfndef(const IdentifierInfo * M,SourceLocation Loc)128 void EnterTopLevelIfndef(const IdentifierInfo *M, SourceLocation Loc) { 129 // If the macro is already set, this is after the top-level #endif. 130 if (TheMacro) 131 return Invalidate(); 132 133 // If we have already expanded a macro by the end of the #ifndef line, then 134 // there is a macro expansion *in* the #ifndef line. This means that the 135 // condition could evaluate differently when subsequently #included. Reject 136 // this. 137 if (DidMacroExpansion) 138 return Invalidate(); 139 140 // Remember that we're in the #if and that we have the macro. 141 ReadAnyTokens = true; 142 ImmediatelyAfterTopLevelIfndef = true; 143 TheMacro = M; 144 MacroLoc = Loc; 145 } 146 147 /// Invoked when a top level conditional (except \#ifndef) is found. EnterTopLevelConditional()148 void EnterTopLevelConditional() { 149 // If a conditional directive (except #ifndef) is found at the top level, 150 // there is a chunk of the file not guarded by the controlling macro. 151 Invalidate(); 152 } 153 154 /// Called when the lexer exits the top-level conditional. ExitTopLevelConditional()155 void ExitTopLevelConditional() { 156 // If we have a macro, that means the top of the file was ok. Set our state 157 // back to "not having read any tokens" so we can detect anything after the 158 // #endif. 159 if (!TheMacro) return Invalidate(); 160 161 // At this point, we haven't "read any tokens" but we do have a controlling 162 // macro. 163 ReadAnyTokens = false; 164 ImmediatelyAfterTopLevelIfndef = false; 165 } 166 167 /// Once the entire file has been lexed, if there is a controlling 168 /// macro, return it. GetControllingMacroAtEndOfFile()169 const IdentifierInfo *GetControllingMacroAtEndOfFile() const { 170 // If we haven't read any tokens after the #endif, return the controlling 171 // macro if it's valid (if it isn't, it will be null). 172 if (!ReadAnyTokens) 173 return TheMacro; 174 return nullptr; 175 } 176 177 /// If the ControllingMacro is followed by a macro definition, return 178 /// the macro that was defined. GetDefinedMacro()179 const IdentifierInfo *GetDefinedMacro() const { 180 return DefinedMacro; 181 } 182 }; 183 184 } // end namespace clang 185 186 #endif 187