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
21namespace 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

source code of clang/include/clang/Lex/VariadicMacroSupport.h