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: VariadicMacroScopeGuard(const Preprocessor & P)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. enterScope()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). exitScope()56 void exitScope() { 57 Ident__VA_ARGS__->setIsPoisoned(true); 58 Ident__VA_OPT__->setIsPoisoned(true); 59 } 60 ~VariadicMacroScopeGuard()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: VAOptDefinitionContext(Preprocessor & PP)74 VAOptDefinitionContext(Preprocessor &PP) 75 : Ident__VA_OPT__(PP.Ident__VA_OPT__) {} 76 isVAOptToken(const Token & T)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 ')'. isInVAOpt()83 bool isInVAOpt() const { return UnmatchedOpeningParens.size(); } 84 85 /// Call this function as soon as you see __VA_OPT__ and '('. sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc)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 getUnmatchedOpeningParenLoc()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. sawClosingParen()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. sawOpeningParen(SourceLocation LParenLoc)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__? isAtTopLevel()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 hasStringifyBefore()139 bool hasStringifyBefore() const { 140 assert(!isReset() && 141 "Must only be called if the state has not been reset"); 142 return StringifyBefore; 143 } 144 isReset()145 bool isReset() const { 146 return NumOfTokensPriorToVAOpt == -1 || 147 VAOptLoc.isInvalid(); 148 } 149 150 public: VAOptExpansionContext(Preprocessor & PP)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 reset()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 getEOFTok()169 const Token &getEOFTok() const { return SyntheticEOFToken; } 170 sawHashOrHashAtBefore(const bool HasLeadingSpace,const bool IsHashAt)171 void sawHashOrHashAtBefore(const bool HasLeadingSpace, 172 const bool IsHashAt) { 173 174 StringifyBefore = !IsHashAt; 175 CharifyBefore = IsHashAt; 176 LeadingSpaceForStringifiedToken = HasLeadingSpace; 177 } 178 hasPlaceholderAfterHashhashAtStart()179 void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; } hasPlaceholderBeforeRParen()180 void hasPlaceholderBeforeRParen() { 181 if (isAtTopLevel()) 182 EndsWithPlaceholder = true; 183 } 184 185 beginsWithPlaceholder()186 bool beginsWithPlaceholder() const { 187 assert(!isReset() && 188 "Must only be called if the state has not been reset"); 189 return BeginsWithPlaceholder; 190 } endsWithPlaceholder()191 bool endsWithPlaceholder() const { 192 assert(!isReset() && 193 "Must only be called if the state has not been reset"); 194 return EndsWithPlaceholder; 195 } 196 hasCharifyBefore()197 bool hasCharifyBefore() const { 198 assert(!isReset() && 199 "Must only be called if the state has not been reset"); 200 return CharifyBefore; 201 } hasStringifyOrCharifyBefore()202 bool hasStringifyOrCharifyBefore() const { 203 return hasStringifyBefore() || hasCharifyBefore(); 204 } 205 getNumberOfTokensPriorToVAOpt()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 getLeadingSpaceForStringifiedToken()212 bool getLeadingSpaceForStringifiedToken() const { 213 assert(hasStringifyBefore() && 214 "Must only be called if this has been marked for stringification"); 215 return LeadingSpaceForStringifiedToken; 216 } 217 sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc,const unsigned int NumPriorTokens)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 getVAOptLoc()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 244