1 //===- VariadicMacroSupport.h - state machines and scope guards -*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines support types to help with preprocessing variadic macro 11 // (i.e. macros that use: ellipses __VA_ARGS__ ) definitions and 12 // expansions. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #ifndef LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H 17 #define LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H 18 19 #include "clang/Lex/Preprocessor.h" 20 #include "llvm/ADT/SmallVector.h" 21 22 namespace clang { 23 class Preprocessor; 24 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__; 35 36 public: 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 " 42 "macro definition!"); 43 assert( 44 !Ident__VA_OPT__ || 45 (Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!")); 46 } 47 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. 50 void enterScope() { 51 Ident__VA_ARGS__->setIsPoisoned(false); 52 if (Ident__VA_OPT__) 53 Ident__VA_OPT__->setIsPoisoned(false); 54 } 55 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 /// occured and the context is being exited. This function is idempotent 59 /// (might be explicitly called, and then reinvoked via the destructor). 60 void exitScope() { 61 Ident__VA_ARGS__->setIsPoisoned(true); 62 if (Ident__VA_OPT__) 63 Ident__VA_OPT__->setIsPoisoned(true); 64 } 65 66 ~VariadicMacroScopeGuard() { exitScope(); } 67 }; 68 69 /// \brief 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; 74 75 const IdentifierInfo *const Ident__VA_OPT__; 76 77 78 public: 79 VAOptDefinitionContext(Preprocessor &PP) 80 : Ident__VA_OPT__(PP.Ident__VA_OPT__) {} 81 82 bool isVAOptToken(const Token &T) const { 83 return Ident__VA_OPT__ && T.getIdentifierInfo() == Ident__VA_OPT__; 84 } 85 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(); } 89 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); 94 95 } 96 97 SourceLocation getUnmatchedOpeningParenLoc() const { 98 assert(isInVAOpt() && "Must be within VAOPT context to call this"); 99 return UnmatchedOpeningParens.back(); 100 } 101 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(); 109 } 110 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); 115 } 116 117 }; 118 119 /// \brief 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 { 122 123 Token SyntheticEOFToken; 124 125 // The (spelling) location of the current __VA_OPT__ in the replacement list 126 // of the function-like macro being expanded. 127 SourceLocation VAOptLoc; 128 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; 134 135 unsigned LeadingSpaceForStringifiedToken : 1; 136 137 unsigned StringifyBefore : 1; 138 unsigned CharifyBefore : 1; 139 140 141 bool hasStringifyBefore() const { 142 assert(!isReset() && 143 "Must only be called if the state has not been reset"); 144 return StringifyBefore; 145 } 146 147 bool isReset() const { 148 return NumOfTokensPriorToVAOpt == -1 || 149 VAOptLoc.isInvalid(); 150 } 151 152 public: 153 VAOptExpansionContext(Preprocessor &PP) 154 : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false), 155 StringifyBefore(false), CharifyBefore(false) { 156 SyntheticEOFToken.startToken(); 157 SyntheticEOFToken.setKind(tok::eof); 158 } 159 160 void reset() { 161 VAOptLoc = SourceLocation(); 162 NumOfTokensPriorToVAOpt = -1; 163 LeadingSpaceForStringifiedToken = false; 164 StringifyBefore = false; 165 CharifyBefore = false; 166 } 167 168 const Token &getEOFTok() const { return SyntheticEOFToken; } 169 170 void sawHashOrHashAtBefore(const bool HasLeadingSpace, 171 const bool IsHashAt) { 172 173 StringifyBefore = !IsHashAt; 174 CharifyBefore = IsHashAt; 175 LeadingSpaceForStringifiedToken = HasLeadingSpace; 176 } 177 178 179 180 bool hasCharifyBefore() const { 181 assert(!isReset() && 182 "Must only be called if the state has not been reset"); 183 return CharifyBefore; 184 } 185 bool hasStringifyOrCharifyBefore() const { 186 return hasStringifyBefore() || hasCharifyBefore(); 187 } 188 189 unsigned int getNumberOfTokensPriorToVAOpt() const { 190 assert(!isReset() && 191 "Must only be called if the state has not been reset"); 192 return NumOfTokensPriorToVAOpt; 193 } 194 195 bool getLeadingSpaceForStringifiedToken() const { 196 assert(hasStringifyBefore() && 197 "Must only be called if this has been marked for stringification"); 198 return LeadingSpaceForStringifiedToken; 199 } 200 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"); 210 } 211 212 SourceLocation getVAOptLoc() const { 213 assert(!isReset() && 214 "Must only be called if the state has not been reset"); 215 assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid"); 216 return VAOptLoc; 217 } 218 using VAOptDefinitionContext::isVAOptToken; 219 using VAOptDefinitionContext::isInVAOpt; 220 using VAOptDefinitionContext::sawClosingParen; 221 using VAOptDefinitionContext::sawOpeningParen; 222 223 }; 224 } // end namespace clang 225 226 #endif 227