1 //===- VariadicMacroSupport.h - state machines and scope guards -*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines support types to help with preprocessing variadic macro
11 // (i.e. macros that use: ellipses __VA_ARGS__ ) definitions and
14 //===----------------------------------------------------------------------===//
16 #ifndef LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H
17 #define LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H
19 #include "clang/Lex/Preprocessor.h"
20 #include "llvm/ADT/SmallVector.h"
25 /// An RAII class that tracks when the Preprocessor starts and stops lexing
26 /// the definition of a (ISO C/C++) variadic macro. As an example, this is
27 /// useful for unpoisoning and repoisoning certain identifiers (such as
28 /// __VA_ARGS__) that are only allowed in this context. Also, being a friend
29 /// of the Preprocessor class allows it to access PP's cached identifiers
30 /// directly (as opposed to performing a lookup each time).
31 class VariadicMacroScopeGuard {
32 const Preprocessor &PP;
33 IdentifierInfo *const Ident__VA_ARGS__;
34 IdentifierInfo *const Ident__VA_OPT__;
37 VariadicMacroScopeGuard(const Preprocessor &P)
38 : PP(P), Ident__VA_ARGS__(PP.Ident__VA_ARGS__),
39 Ident__VA_OPT__(PP.Ident__VA_OPT__) {
40 assert(Ident__VA_ARGS__->isPoisoned() && "__VA_ARGS__ should be poisoned "
41 "outside an ISO C/C++ variadic "
45 (Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!"));
48 /// Client code should call this function just before the Preprocessor is
49 /// about to Lex tokens from the definition of a variadic (ISO C/C++) macro.
51 Ident__VA_ARGS__->setIsPoisoned(false);
53 Ident__VA_OPT__->setIsPoisoned(false);
56 /// Client code should call this function as soon as the Preprocessor has
57 /// either completed lexing the macro's definition tokens, or an error
58 /// occurred and the context is being exited. This function is idempotent
59 /// (might be explicitly called, and then reinvoked via the destructor).
61 Ident__VA_ARGS__->setIsPoisoned(true);
63 Ident__VA_OPT__->setIsPoisoned(true);
66 ~VariadicMacroScopeGuard() { exitScope(); }
69 /// A class for tracking whether we're inside a VA_OPT during a
70 /// traversal of the tokens of a variadic macro definition.
71 class VAOptDefinitionContext {
72 /// Contains all the locations of so far unmatched lparens.
73 SmallVector<SourceLocation, 8> UnmatchedOpeningParens;
75 const IdentifierInfo *const Ident__VA_OPT__;
79 VAOptDefinitionContext(Preprocessor &PP)
80 : Ident__VA_OPT__(PP.Ident__VA_OPT__) {}
82 bool isVAOptToken(const Token &T) const {
83 return Ident__VA_OPT__ && T.getIdentifierInfo() == Ident__VA_OPT__;
86 /// Returns true if we have seen the __VA_OPT__ and '(' but before having
87 /// seen the matching ')'.
88 bool isInVAOpt() const { return UnmatchedOpeningParens.size(); }
90 /// Call this function as soon as you see __VA_OPT__ and '('.
91 void sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc) {
92 assert(!isInVAOpt() && "Must NOT be within VAOPT context to call this");
93 UnmatchedOpeningParens.push_back(LParenLoc);
97 SourceLocation getUnmatchedOpeningParenLoc() const {
98 assert(isInVAOpt() && "Must be within VAOPT context to call this");
99 return UnmatchedOpeningParens.back();
102 /// Call this function each time an rparen is seen. It returns true only if
103 /// the rparen that was just seen was the eventual (non-nested) closing
104 /// paren for VAOPT, and ejects us out of the VAOPT context.
105 bool sawClosingParen() {
106 assert(isInVAOpt() && "Must be within VAOPT context to call this");
107 UnmatchedOpeningParens.pop_back();
108 return !UnmatchedOpeningParens.size();
111 /// Call this function each time an lparen is seen.
112 void sawOpeningParen(SourceLocation LParenLoc) {
113 assert(isInVAOpt() && "Must be within VAOPT context to call this");
114 UnmatchedOpeningParens.push_back(LParenLoc);
119 /// A class for tracking whether we're inside a VA_OPT during a
120 /// traversal of the tokens of a macro during macro expansion.
121 class VAOptExpansionContext : VAOptDefinitionContext {
123 Token SyntheticEOFToken;
125 // The (spelling) location of the current __VA_OPT__ in the replacement list
126 // of the function-like macro being expanded.
127 SourceLocation VAOptLoc;
129 // NumOfTokensPriorToVAOpt : when != -1, contains the index *of* the first
130 // token of the current VAOPT contents (so we know where to start eager
131 // token-pasting and stringification) *within* the substituted tokens of
132 // the function-like macro's new replacement list.
133 int NumOfTokensPriorToVAOpt = -1;
135 unsigned LeadingSpaceForStringifiedToken : 1;
137 unsigned StringifyBefore : 1;
138 unsigned CharifyBefore : 1;
141 bool hasStringifyBefore() const {
143 "Must only be called if the state has not been reset");
144 return StringifyBefore;
147 bool isReset() const {
148 return NumOfTokensPriorToVAOpt == -1 ||
149 VAOptLoc.isInvalid();
153 VAOptExpansionContext(Preprocessor &PP)
154 : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false),
155 StringifyBefore(false), CharifyBefore(false) {
156 SyntheticEOFToken.startToken();
157 SyntheticEOFToken.setKind(tok::eof);
161 VAOptLoc = SourceLocation();
162 NumOfTokensPriorToVAOpt = -1;
163 LeadingSpaceForStringifiedToken = false;
164 StringifyBefore = false;
165 CharifyBefore = false;
168 const Token &getEOFTok() const { return SyntheticEOFToken; }
170 void sawHashOrHashAtBefore(const bool HasLeadingSpace,
171 const bool IsHashAt) {
173 StringifyBefore = !IsHashAt;
174 CharifyBefore = IsHashAt;
175 LeadingSpaceForStringifiedToken = HasLeadingSpace;
180 bool hasCharifyBefore() const {
182 "Must only be called if the state has not been reset");
183 return CharifyBefore;
185 bool hasStringifyOrCharifyBefore() const {
186 return hasStringifyBefore() || hasCharifyBefore();
189 unsigned int getNumberOfTokensPriorToVAOpt() const {
191 "Must only be called if the state has not been reset");
192 return NumOfTokensPriorToVAOpt;
195 bool getLeadingSpaceForStringifiedToken() const {
196 assert(hasStringifyBefore() &&
197 "Must only be called if this has been marked for stringification");
198 return LeadingSpaceForStringifiedToken;
201 void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc,
202 const unsigned int NumPriorTokens) {
203 assert(VAOptLoc.isFileID() && "Must not come from a macro expansion");
204 assert(isReset() && "Must only be called if the state has been reset");
205 VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(SourceLocation());
206 this->VAOptLoc = VAOptLoc;
207 NumOfTokensPriorToVAOpt = NumPriorTokens;
208 assert(NumOfTokensPriorToVAOpt > -1 &&
209 "Too many prior tokens");
212 SourceLocation getVAOptLoc() const {
214 "Must only be called if the state has not been reset");
215 assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid");
218 using VAOptDefinitionContext::isVAOptToken;
219 using VAOptDefinitionContext::isInVAOpt;
220 using VAOptDefinitionContext::sawClosingParen;
221 using VAOptDefinitionContext::sawOpeningParen;
224 } // end namespace clang