Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 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(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(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 | unsigned LeadingSpaceForStringifiedToken : 1; |
||
133 | |||
134 | unsigned StringifyBefore : 1; |
||
135 | unsigned CharifyBefore : 1; |
||
136 | unsigned BeginsWithPlaceholder : 1; |
||
137 | unsigned EndsWithPlaceholder : 1; |
||
138 | |||
139 | bool hasStringifyBefore() const { |
||
140 | assert(!isReset() && |
||
141 | "Must only be called if the state has not been reset"); |
||
142 | return StringifyBefore; |
||
143 | } |
||
144 | |||
145 | bool isReset() const { |
||
146 | return NumOfTokensPriorToVAOpt == -1 || |
||
147 | VAOptLoc.isInvalid(); |
||
148 | } |
||
149 | |||
150 | public: |
||
151 | VAOptExpansionContext(Preprocessor &PP) |
||
152 | : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false), |
||
153 | StringifyBefore(false), CharifyBefore(false), |
||
154 | BeginsWithPlaceholder(false), EndsWithPlaceholder(false) { |
||
155 | SyntheticEOFToken.startToken(); |
||
156 | SyntheticEOFToken.setKind(tok::eof); |
||
157 | } |
||
158 | |||
159 | void reset() { |
||
160 | VAOptLoc = SourceLocation(); |
||
161 | NumOfTokensPriorToVAOpt = -1; |
||
162 | LeadingSpaceForStringifiedToken = false; |
||
163 | StringifyBefore = false; |
||
164 | CharifyBefore = false; |
||
165 | BeginsWithPlaceholder = false; |
||
166 | EndsWithPlaceholder = false; |
||
167 | } |
||
168 | |||
169 | const Token &getEOFTok() const { return SyntheticEOFToken; } |
||
170 | |||
171 | void sawHashOrHashAtBefore(const bool HasLeadingSpace, |
||
172 | const bool IsHashAt) { |
||
173 | |||
174 | StringifyBefore = !IsHashAt; |
||
175 | CharifyBefore = IsHashAt; |
||
176 | LeadingSpaceForStringifiedToken = HasLeadingSpace; |
||
177 | } |
||
178 | |||
179 | void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; } |
||
180 | void hasPlaceholderBeforeRParen() { |
||
181 | if (isAtTopLevel()) |
||
182 | EndsWithPlaceholder = true; |
||
183 | } |
||
184 | |||
185 | |||
186 | bool beginsWithPlaceholder() const { |
||
187 | assert(!isReset() && |
||
188 | "Must only be called if the state has not been reset"); |
||
189 | return BeginsWithPlaceholder; |
||
190 | } |
||
191 | bool endsWithPlaceholder() const { |
||
192 | assert(!isReset() && |
||
193 | "Must only be called if the state has not been reset"); |
||
194 | return EndsWithPlaceholder; |
||
195 | } |
||
196 | |||
197 | bool hasCharifyBefore() const { |
||
198 | assert(!isReset() && |
||
199 | "Must only be called if the state has not been reset"); |
||
200 | return CharifyBefore; |
||
201 | } |
||
202 | bool hasStringifyOrCharifyBefore() const { |
||
203 | return hasStringifyBefore() || hasCharifyBefore(); |
||
204 | } |
||
205 | |||
206 | unsigned int getNumberOfTokensPriorToVAOpt() const { |
||
207 | assert(!isReset() && |
||
208 | "Must only be called if the state has not been reset"); |
||
209 | return NumOfTokensPriorToVAOpt; |
||
210 | } |
||
211 | |||
212 | bool getLeadingSpaceForStringifiedToken() const { |
||
213 | assert(hasStringifyBefore() && |
||
214 | "Must only be called if this has been marked for stringification"); |
||
215 | return LeadingSpaceForStringifiedToken; |
||
216 | } |
||
217 | |||
218 | void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc, |
||
219 | const unsigned int NumPriorTokens) { |
||
220 | assert(VAOptLoc.isFileID() && "Must not come from a macro expansion"); |
||
221 | assert(isReset() && "Must only be called if the state has been reset"); |
||
222 | VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(SourceLocation()); |
||
223 | this->VAOptLoc = VAOptLoc; |
||
224 | NumOfTokensPriorToVAOpt = NumPriorTokens; |
||
225 | assert(NumOfTokensPriorToVAOpt > -1 && |
||
226 | "Too many prior tokens"); |
||
227 | } |
||
228 | |||
229 | SourceLocation getVAOptLoc() const { |
||
230 | assert(!isReset() && |
||
231 | "Must only be called if the state has not been reset"); |
||
232 | assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid"); |
||
233 | return VAOptLoc; |
||
234 | } |
||
235 | using VAOptDefinitionContext::isVAOptToken; |
||
236 | using VAOptDefinitionContext::isInVAOpt; |
||
237 | using VAOptDefinitionContext::sawClosingParen; |
||
238 | using VAOptDefinitionContext::sawOpeningParen; |
||
239 | |||
240 | }; |
||
241 | } // end namespace clang |
||
242 | |||
243 | #endif |