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
19namespace clang {
20class 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.
28class 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;
57public:
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