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