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: |
58 | MultipleIncludeOpt() { |
59 | ReadAnyTokens = false; |
60 | ImmediatelyAfterTopLevelIfndef = false; |
61 | DidMacroExpansion = false; |
62 | TheMacro = nullptr; |
63 | DefinedMacro = nullptr; |
64 | } |
65 | |
66 | SourceLocation GetMacroLocation() const { |
67 | return MacroLoc; |
68 | } |
69 | |
70 | SourceLocation GetDefinedLocation() const { |
71 | return DefinedLoc; |
72 | } |
73 | |
74 | void resetImmediatelyAfterTopLevelIfndef() { |
75 | ImmediatelyAfterTopLevelIfndef = false; |
76 | } |
77 | |
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. |
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. |
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. |
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. |
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). |
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. |
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. |
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. |
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. |
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. |
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. |
179 | const IdentifierInfo *GetDefinedMacro() const { |
180 | return DefinedMacro; |
181 | } |
182 | }; |
183 | |
184 | } // end namespace clang |
185 | |
186 | #endif |
187 | |