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( 43 !Ident__VA_OPT__ || 44 (Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!")); 45 } 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. enterScope()49 void enterScope() { 50 Ident__VA_ARGS__->setIsPoisoned(false); 51 if (Ident__VA_OPT__) 52 Ident__VA_OPT__->setIsPoisoned(false); 53 } 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). exitScope()59 void exitScope() { 60 Ident__VA_ARGS__->setIsPoisoned(true); 61 if (Ident__VA_OPT__) 62 Ident__VA_OPT__->setIsPoisoned(true); 63 } 64 ~VariadicMacroScopeGuard()65 ~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: VAOptDefinitionContext(Preprocessor & PP)78 VAOptDefinitionContext(Preprocessor &PP) 79 : Ident__VA_OPT__(PP.Ident__VA_OPT__) {} 80 isVAOptToken(const Token & T)81 bool isVAOptToken(const Token &T) const { 82 return Ident__VA_OPT__ && T.getIdentifierInfo() == Ident__VA_OPT__; 83 } 84 85 /// Returns true if we have seen the __VA_OPT__ and '(' but before having 86 /// seen the matching ')'. isInVAOpt()87 bool isInVAOpt() const { return UnmatchedOpeningParens.size(); } 88 89 /// Call this function as soon as you see __VA_OPT__ and '('. sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc)90 void sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc) { 91 assert(!isInVAOpt() && "Must NOT be within VAOPT context to call this"); 92 UnmatchedOpeningParens.push_back(LParenLoc); 93 94 } 95 getUnmatchedOpeningParenLoc()96 SourceLocation getUnmatchedOpeningParenLoc() const { 97 assert(isInVAOpt() && "Must be within VAOPT context to call this"); 98 return UnmatchedOpeningParens.back(); 99 } 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. sawClosingParen()104 bool sawClosingParen() { 105 assert(isInVAOpt() && "Must be within VAOPT context to call this"); 106 UnmatchedOpeningParens.pop_back(); 107 return !UnmatchedOpeningParens.size(); 108 } 109 110 /// Call this function each time an lparen is seen. sawOpeningParen(SourceLocation LParenLoc)111 void sawOpeningParen(SourceLocation LParenLoc) { 112 assert(isInVAOpt() && "Must be within VAOPT context to call this"); 113 UnmatchedOpeningParens.push_back(LParenLoc); 114 } 115 116 /// Are we at the top level within the __VA_OPT__? isAtTopLevel()117 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 hasStringifyBefore()143 bool hasStringifyBefore() const { 144 assert(!isReset() && 145 "Must only be called if the state has not been reset"); 146 return StringifyBefore; 147 } 148 isReset()149 bool isReset() const { 150 return NumOfTokensPriorToVAOpt == -1 || 151 VAOptLoc.isInvalid(); 152 } 153 154 public: VAOptExpansionContext(Preprocessor & PP)155 VAOptExpansionContext(Preprocessor &PP) 156 : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false), 157 StringifyBefore(false), CharifyBefore(false), 158 BeginsWithPlaceholder(false), EndsWithPlaceholder(false) { 159 SyntheticEOFToken.startToken(); 160 SyntheticEOFToken.setKind(tok::eof); 161 } 162 reset()163 void reset() { 164 VAOptLoc = SourceLocation(); 165 NumOfTokensPriorToVAOpt = -1; 166 LeadingSpaceForStringifiedToken = false; 167 StringifyBefore = false; 168 CharifyBefore = false; 169 BeginsWithPlaceholder = false; 170 EndsWithPlaceholder = false; 171 } 172 getEOFTok()173 const Token &getEOFTok() const { return SyntheticEOFToken; } 174 sawHashOrHashAtBefore(const bool HasLeadingSpace,const bool IsHashAt)175 void sawHashOrHashAtBefore(const bool HasLeadingSpace, 176 const bool IsHashAt) { 177 178 StringifyBefore = !IsHashAt; 179 CharifyBefore = IsHashAt; 180 LeadingSpaceForStringifiedToken = HasLeadingSpace; 181 } 182 hasPlaceholderAfterHashhashAtStart()183 void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; } hasPlaceholderBeforeRParen()184 void hasPlaceholderBeforeRParen() { 185 if (isAtTopLevel()) 186 EndsWithPlaceholder = true; 187 } 188 189 beginsWithPlaceholder()190 bool beginsWithPlaceholder() const { 191 assert(!isReset() && 192 "Must only be called if the state has not been reset"); 193 return BeginsWithPlaceholder; 194 } endsWithPlaceholder()195 bool endsWithPlaceholder() const { 196 assert(!isReset() && 197 "Must only be called if the state has not been reset"); 198 return EndsWithPlaceholder; 199 } 200 hasCharifyBefore()201 bool hasCharifyBefore() const { 202 assert(!isReset() && 203 "Must only be called if the state has not been reset"); 204 return CharifyBefore; 205 } hasStringifyOrCharifyBefore()206 bool hasStringifyOrCharifyBefore() const { 207 return hasStringifyBefore() || hasCharifyBefore(); 208 } 209 getNumberOfTokensPriorToVAOpt()210 unsigned int getNumberOfTokensPriorToVAOpt() const { 211 assert(!isReset() && 212 "Must only be called if the state has not been reset"); 213 return NumOfTokensPriorToVAOpt; 214 } 215 getLeadingSpaceForStringifiedToken()216 bool getLeadingSpaceForStringifiedToken() const { 217 assert(hasStringifyBefore() && 218 "Must only be called if this has been marked for stringification"); 219 return LeadingSpaceForStringifiedToken; 220 } 221 sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc,const unsigned int NumPriorTokens)222 void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc, 223 const unsigned int NumPriorTokens) { 224 assert(VAOptLoc.isFileID() && "Must not come from a macro expansion"); 225 assert(isReset() && "Must only be called if the state has been reset"); 226 VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(SourceLocation()); 227 this->VAOptLoc = VAOptLoc; 228 NumOfTokensPriorToVAOpt = NumPriorTokens; 229 assert(NumOfTokensPriorToVAOpt > -1 && 230 "Too many prior tokens"); 231 } 232 getVAOptLoc()233 SourceLocation getVAOptLoc() const { 234 assert(!isReset() && 235 "Must only be called if the state has not been reset"); 236 assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid"); 237 return VAOptLoc; 238 } 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 248