xref: /freebsd/contrib/llvm-project/clang/include/clang/Lex/MultipleIncludeOpt.h (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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