Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 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 | /// ExpandedMacro - When a macro is expanded with this lexer as the current |
||
112 | /// buffer, this method is called to disable the MIOpt if needed. |
||
113 | void ExpandedMacro() { DidMacroExpansion = true; } |
||
114 | |||
115 | /// Called when entering a top-level \#ifndef directive (or the |
||
116 | /// "\#if !defined" equivalent) without any preceding tokens. |
||
117 | /// |
||
118 | /// Note, we don't care about the input value of 'ReadAnyTokens'. The caller |
||
119 | /// ensures that this is only called if there are no tokens read before the |
||
120 | /// \#ifndef. The caller is required to do this, because reading the \#if |
||
121 | /// line obviously reads in tokens. |
||
122 | void EnterTopLevelIfndef(const IdentifierInfo *M, SourceLocation Loc) { |
||
123 | // If the macro is already set, this is after the top-level #endif. |
||
124 | if (TheMacro) |
||
125 | return Invalidate(); |
||
126 | |||
127 | // If we have already expanded a macro by the end of the #ifndef line, then |
||
128 | // there is a macro expansion *in* the #ifndef line. This means that the |
||
129 | // condition could evaluate differently when subsequently #included. Reject |
||
130 | // this. |
||
131 | if (DidMacroExpansion) |
||
132 | return Invalidate(); |
||
133 | |||
134 | // Remember that we're in the #if and that we have the macro. |
||
135 | ReadAnyTokens = true; |
||
136 | ImmediatelyAfterTopLevelIfndef = true; |
||
137 | TheMacro = M; |
||
138 | MacroLoc = Loc; |
||
139 | } |
||
140 | |||
141 | /// Invoked when a top level conditional (except \#ifndef) is found. |
||
142 | void EnterTopLevelConditional() { |
||
143 | // If a conditional directive (except #ifndef) is found at the top level, |
||
144 | // there is a chunk of the file not guarded by the controlling macro. |
||
145 | Invalidate(); |
||
146 | } |
||
147 | |||
148 | /// Called when the lexer exits the top-level conditional. |
||
149 | void ExitTopLevelConditional() { |
||
150 | // If we have a macro, that means the top of the file was ok. Set our state |
||
151 | // back to "not having read any tokens" so we can detect anything after the |
||
152 | // #endif. |
||
153 | if (!TheMacro) return Invalidate(); |
||
154 | |||
155 | // At this point, we haven't "read any tokens" but we do have a controlling |
||
156 | // macro. |
||
157 | ReadAnyTokens = false; |
||
158 | ImmediatelyAfterTopLevelIfndef = false; |
||
159 | } |
||
160 | |||
161 | /// Once the entire file has been lexed, if there is a controlling |
||
162 | /// macro, return it. |
||
163 | const IdentifierInfo *GetControllingMacroAtEndOfFile() const { |
||
164 | // If we haven't read any tokens after the #endif, return the controlling |
||
165 | // macro if it's valid (if it isn't, it will be null). |
||
166 | if (!ReadAnyTokens) |
||
167 | return TheMacro; |
||
168 | return nullptr; |
||
169 | } |
||
170 | |||
171 | /// If the ControllingMacro is followed by a macro definition, return |
||
172 | /// the macro that was defined. |
||
173 | const IdentifierInfo *GetDefinedMacro() const { |
||
174 | return DefinedMacro; |
||
175 | } |
||
176 | }; |
||
177 | |||
178 | } // end namespace clang |
||
179 | |||
180 | #endif |