1 | //===- VariadicMacroSupport.h - state machines and scope guards -*- 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 | // This file defines support types to help with preprocessing variadic macro |
10 | // (i.e. macros that use: ellipses __VA_ARGS__ ) definitions and |
11 | // expansions. |
12 | // |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #ifndef LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H |
16 | #define LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H |
17 | |
18 | #include "clang/Lex/Preprocessor.h" |
19 | #include "llvm/ADT/SmallVector.h" |
20 | |
21 | namespace clang { |
22 | class Preprocessor; |
23 | |
24 | /// An RAII class that tracks when the Preprocessor starts and stops lexing |
25 | /// the definition of a (ISO C/C++) variadic macro. As an example, this is |
26 | /// useful for unpoisoning and repoisoning certain identifiers (such as |
27 | /// __VA_ARGS__) that are only allowed in this context. Also, being a friend |
28 | /// of the Preprocessor class allows it to access PP's cached identifiers |
29 | /// directly (as opposed to performing a lookup each time). |
30 | class VariadicMacroScopeGuard { |
31 | const Preprocessor &PP; |
32 | IdentifierInfo *const Ident__VA_ARGS__; |
33 | IdentifierInfo *const Ident__VA_OPT__; |
34 | |
35 | public: |
36 | VariadicMacroScopeGuard(const Preprocessor &P) |
37 | : PP(P), Ident__VA_ARGS__(PP.Ident__VA_ARGS__), |
38 | Ident__VA_OPT__(PP.Ident__VA_OPT__) { |
39 | assert(Ident__VA_ARGS__->isPoisoned() && "__VA_ARGS__ should be poisoned " |
40 | "outside an ISO C/C++ variadic " |
41 | "macro definition!" ); |
42 | assert(Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!" ); |
43 | } |
44 | |
45 | /// Client code should call this function just before the Preprocessor is |
46 | /// about to Lex tokens from the definition of a variadic (ISO C/C++) macro. |
47 | void enterScope() { |
48 | Ident__VA_ARGS__->setIsPoisoned(false); |
49 | Ident__VA_OPT__->setIsPoisoned(false); |
50 | } |
51 | |
52 | /// Client code should call this function as soon as the Preprocessor has |
53 | /// either completed lexing the macro's definition tokens, or an error |
54 | /// occurred and the context is being exited. This function is idempotent |
55 | /// (might be explicitly called, and then reinvoked via the destructor). |
56 | void exitScope() { |
57 | Ident__VA_ARGS__->setIsPoisoned(true); |
58 | Ident__VA_OPT__->setIsPoisoned(true); |
59 | } |
60 | |
61 | ~VariadicMacroScopeGuard() { exitScope(); } |
62 | }; |
63 | |
64 | /// A class for tracking whether we're inside a VA_OPT during a |
65 | /// traversal of the tokens of a variadic macro definition. |
66 | class VAOptDefinitionContext { |
67 | /// Contains all the locations of so far unmatched lparens. |
68 | SmallVector<SourceLocation, 8> UnmatchedOpeningParens; |
69 | |
70 | const IdentifierInfo *const Ident__VA_OPT__; |
71 | |
72 | |
73 | public: |
74 | VAOptDefinitionContext(Preprocessor &PP) |
75 | : Ident__VA_OPT__(PP.Ident__VA_OPT__) {} |
76 | |
77 | bool isVAOptToken(const Token &T) const { |
78 | return Ident__VA_OPT__ && T.getIdentifierInfo() == Ident__VA_OPT__; |
79 | } |
80 | |
81 | /// Returns true if we have seen the __VA_OPT__ and '(' but before having |
82 | /// seen the matching ')'. |
83 | bool isInVAOpt() const { return UnmatchedOpeningParens.size(); } |
84 | |
85 | /// Call this function as soon as you see __VA_OPT__ and '('. |
86 | void sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc) { |
87 | assert(!isInVAOpt() && "Must NOT be within VAOPT context to call this" ); |
88 | UnmatchedOpeningParens.push_back(Elt: LParenLoc); |
89 | |
90 | } |
91 | |
92 | SourceLocation getUnmatchedOpeningParenLoc() const { |
93 | assert(isInVAOpt() && "Must be within VAOPT context to call this" ); |
94 | return UnmatchedOpeningParens.back(); |
95 | } |
96 | |
97 | /// Call this function each time an rparen is seen. It returns true only if |
98 | /// the rparen that was just seen was the eventual (non-nested) closing |
99 | /// paren for VAOPT, and ejects us out of the VAOPT context. |
100 | bool sawClosingParen() { |
101 | assert(isInVAOpt() && "Must be within VAOPT context to call this" ); |
102 | UnmatchedOpeningParens.pop_back(); |
103 | return !UnmatchedOpeningParens.size(); |
104 | } |
105 | |
106 | /// Call this function each time an lparen is seen. |
107 | void sawOpeningParen(SourceLocation LParenLoc) { |
108 | assert(isInVAOpt() && "Must be within VAOPT context to call this" ); |
109 | UnmatchedOpeningParens.push_back(Elt: LParenLoc); |
110 | } |
111 | |
112 | /// Are we at the top level within the __VA_OPT__? |
113 | bool isAtTopLevel() const { return UnmatchedOpeningParens.size() == 1; } |
114 | }; |
115 | |
116 | /// A class for tracking whether we're inside a VA_OPT during a |
117 | /// traversal of the tokens of a macro during macro expansion. |
118 | class VAOptExpansionContext : VAOptDefinitionContext { |
119 | |
120 | Token SyntheticEOFToken; |
121 | |
122 | // The (spelling) location of the current __VA_OPT__ in the replacement list |
123 | // of the function-like macro being expanded. |
124 | SourceLocation VAOptLoc; |
125 | |
126 | // NumOfTokensPriorToVAOpt : when != -1, contains the index *of* the first |
127 | // token of the current VAOPT contents (so we know where to start eager |
128 | // token-pasting and stringification) *within* the substituted tokens of |
129 | // the function-like macro's new replacement list. |
130 | int NumOfTokensPriorToVAOpt = -1; |
131 | |
132 | LLVM_PREFERRED_TYPE(bool) |
133 | unsigned LeadingSpaceForStringifiedToken : 1; |
134 | |
135 | LLVM_PREFERRED_TYPE(bool) |
136 | unsigned StringifyBefore : 1; |
137 | LLVM_PREFERRED_TYPE(bool) |
138 | unsigned CharifyBefore : 1; |
139 | LLVM_PREFERRED_TYPE(bool) |
140 | unsigned BeginsWithPlaceholder : 1; |
141 | LLVM_PREFERRED_TYPE(bool) |
142 | unsigned EndsWithPlaceholder : 1; |
143 | |
144 | bool hasStringifyBefore() const { |
145 | assert(!isReset() && |
146 | "Must only be called if the state has not been reset" ); |
147 | return StringifyBefore; |
148 | } |
149 | |
150 | bool isReset() const { |
151 | return NumOfTokensPriorToVAOpt == -1 || |
152 | VAOptLoc.isInvalid(); |
153 | } |
154 | |
155 | public: |
156 | VAOptExpansionContext(Preprocessor &PP) |
157 | : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false), |
158 | StringifyBefore(false), CharifyBefore(false), |
159 | BeginsWithPlaceholder(false), EndsWithPlaceholder(false) { |
160 | SyntheticEOFToken.startToken(); |
161 | SyntheticEOFToken.setKind(tok::eof); |
162 | } |
163 | |
164 | void reset() { |
165 | VAOptLoc = SourceLocation(); |
166 | NumOfTokensPriorToVAOpt = -1; |
167 | LeadingSpaceForStringifiedToken = false; |
168 | StringifyBefore = false; |
169 | CharifyBefore = false; |
170 | BeginsWithPlaceholder = false; |
171 | EndsWithPlaceholder = false; |
172 | } |
173 | |
174 | const Token &getEOFTok() const { return SyntheticEOFToken; } |
175 | |
176 | void sawHashOrHashAtBefore(const bool HasLeadingSpace, |
177 | const bool IsHashAt) { |
178 | |
179 | StringifyBefore = !IsHashAt; |
180 | CharifyBefore = IsHashAt; |
181 | LeadingSpaceForStringifiedToken = HasLeadingSpace; |
182 | } |
183 | |
184 | void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; } |
185 | void hasPlaceholderBeforeRParen() { |
186 | if (isAtTopLevel()) |
187 | EndsWithPlaceholder = true; |
188 | } |
189 | |
190 | |
191 | bool beginsWithPlaceholder() const { |
192 | assert(!isReset() && |
193 | "Must only be called if the state has not been reset" ); |
194 | return BeginsWithPlaceholder; |
195 | } |
196 | bool endsWithPlaceholder() const { |
197 | assert(!isReset() && |
198 | "Must only be called if the state has not been reset" ); |
199 | return EndsWithPlaceholder; |
200 | } |
201 | |
202 | bool hasCharifyBefore() const { |
203 | assert(!isReset() && |
204 | "Must only be called if the state has not been reset" ); |
205 | return CharifyBefore; |
206 | } |
207 | bool hasStringifyOrCharifyBefore() const { |
208 | return hasStringifyBefore() || hasCharifyBefore(); |
209 | } |
210 | |
211 | unsigned int getNumberOfTokensPriorToVAOpt() const { |
212 | assert(!isReset() && |
213 | "Must only be called if the state has not been reset" ); |
214 | return NumOfTokensPriorToVAOpt; |
215 | } |
216 | |
217 | bool getLeadingSpaceForStringifiedToken() const { |
218 | assert(hasStringifyBefore() && |
219 | "Must only be called if this has been marked for stringification" ); |
220 | return LeadingSpaceForStringifiedToken; |
221 | } |
222 | |
223 | void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc, |
224 | const unsigned int NumPriorTokens) { |
225 | assert(VAOptLoc.isFileID() && "Must not come from a macro expansion" ); |
226 | assert(isReset() && "Must only be called if the state has been reset" ); |
227 | VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(LParenLoc: SourceLocation()); |
228 | this->VAOptLoc = VAOptLoc; |
229 | NumOfTokensPriorToVAOpt = NumPriorTokens; |
230 | assert(NumOfTokensPriorToVAOpt > -1 && |
231 | "Too many prior tokens" ); |
232 | } |
233 | |
234 | SourceLocation getVAOptLoc() const { |
235 | assert(!isReset() && |
236 | "Must only be called if the state has not been reset" ); |
237 | assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid" ); |
238 | return VAOptLoc; |
239 | } |
240 | using VAOptDefinitionContext::isVAOptToken; |
241 | using VAOptDefinitionContext::isInVAOpt; |
242 | using VAOptDefinitionContext::sawClosingParen; |
243 | using VAOptDefinitionContext::sawOpeningParen; |
244 | |
245 | }; |
246 | } // end namespace clang |
247 | |
248 | #endif |
249 | |