//===- VariadicMacroSupport.h - state machines and scope guards -*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines support types to help with preprocessing variadic macro // (i.e. macros that use: ellipses __VA_ARGS__ ) definitions and // expansions. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H #define LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/SmallVector.h" namespace clang { class Preprocessor; /// An RAII class that tracks when the Preprocessor starts and stops lexing /// the definition of a (ISO C/C++) variadic macro. As an example, this is /// useful for unpoisoning and repoisoning certain identifiers (such as /// __VA_ARGS__) that are only allowed in this context. Also, being a friend /// of the Preprocessor class allows it to access PP's cached identifiers /// directly (as opposed to performing a lookup each time). class VariadicMacroScopeGuard { const Preprocessor &PP; IdentifierInfo *const Ident__VA_ARGS__; IdentifierInfo *const Ident__VA_OPT__; public: VariadicMacroScopeGuard(const Preprocessor &P) : PP(P), Ident__VA_ARGS__(PP.Ident__VA_ARGS__), Ident__VA_OPT__(PP.Ident__VA_OPT__) { assert(Ident__VA_ARGS__->isPoisoned() && "__VA_ARGS__ should be poisoned " "outside an ISO C/C++ variadic " "macro definition!"); assert( !Ident__VA_OPT__ || (Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!")); } /// Client code should call this function just before the Preprocessor is /// about to Lex tokens from the definition of a variadic (ISO C/C++) macro. void enterScope() { Ident__VA_ARGS__->setIsPoisoned(false); if (Ident__VA_OPT__) Ident__VA_OPT__->setIsPoisoned(false); } /// Client code should call this function as soon as the Preprocessor has /// either completed lexing the macro's definition tokens, or an error /// occurred and the context is being exited. This function is idempotent /// (might be explicitly called, and then reinvoked via the destructor). void exitScope() { Ident__VA_ARGS__->setIsPoisoned(true); if (Ident__VA_OPT__) Ident__VA_OPT__->setIsPoisoned(true); } ~VariadicMacroScopeGuard() { exitScope(); } }; /// A class for tracking whether we're inside a VA_OPT during a /// traversal of the tokens of a variadic macro definition. class VAOptDefinitionContext { /// Contains all the locations of so far unmatched lparens. SmallVector UnmatchedOpeningParens; const IdentifierInfo *const Ident__VA_OPT__; public: VAOptDefinitionContext(Preprocessor &PP) : Ident__VA_OPT__(PP.Ident__VA_OPT__) {} bool isVAOptToken(const Token &T) const { return Ident__VA_OPT__ && T.getIdentifierInfo() == Ident__VA_OPT__; } /// Returns true if we have seen the __VA_OPT__ and '(' but before having /// seen the matching ')'. bool isInVAOpt() const { return UnmatchedOpeningParens.size(); } /// Call this function as soon as you see __VA_OPT__ and '('. void sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc) { assert(!isInVAOpt() && "Must NOT be within VAOPT context to call this"); UnmatchedOpeningParens.push_back(LParenLoc); } SourceLocation getUnmatchedOpeningParenLoc() const { assert(isInVAOpt() && "Must be within VAOPT context to call this"); return UnmatchedOpeningParens.back(); } /// Call this function each time an rparen is seen. It returns true only if /// the rparen that was just seen was the eventual (non-nested) closing /// paren for VAOPT, and ejects us out of the VAOPT context. bool sawClosingParen() { assert(isInVAOpt() && "Must be within VAOPT context to call this"); UnmatchedOpeningParens.pop_back(); return !UnmatchedOpeningParens.size(); } /// Call this function each time an lparen is seen. void sawOpeningParen(SourceLocation LParenLoc) { assert(isInVAOpt() && "Must be within VAOPT context to call this"); UnmatchedOpeningParens.push_back(LParenLoc); } /// Are we at the top level within the __VA_OPT__? bool isAtTopLevel() const { return UnmatchedOpeningParens.size() == 1; } }; /// A class for tracking whether we're inside a VA_OPT during a /// traversal of the tokens of a macro during macro expansion. class VAOptExpansionContext : VAOptDefinitionContext { Token SyntheticEOFToken; // The (spelling) location of the current __VA_OPT__ in the replacement list // of the function-like macro being expanded. SourceLocation VAOptLoc; // NumOfTokensPriorToVAOpt : when != -1, contains the index *of* the first // token of the current VAOPT contents (so we know where to start eager // token-pasting and stringification) *within* the substituted tokens of // the function-like macro's new replacement list. int NumOfTokensPriorToVAOpt = -1; unsigned LeadingSpaceForStringifiedToken : 1; unsigned StringifyBefore : 1; unsigned CharifyBefore : 1; unsigned BeginsWithPlaceholder : 1; unsigned EndsWithPlaceholder : 1; bool hasStringifyBefore() const { assert(!isReset() && "Must only be called if the state has not been reset"); return StringifyBefore; } bool isReset() const { return NumOfTokensPriorToVAOpt == -1 || VAOptLoc.isInvalid(); } public: VAOptExpansionContext(Preprocessor &PP) : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false), StringifyBefore(false), CharifyBefore(false), BeginsWithPlaceholder(false), EndsWithPlaceholder(false) { SyntheticEOFToken.startToken(); SyntheticEOFToken.setKind(tok::eof); } void reset() { VAOptLoc = SourceLocation(); NumOfTokensPriorToVAOpt = -1; LeadingSpaceForStringifiedToken = false; StringifyBefore = false; CharifyBefore = false; BeginsWithPlaceholder = false; EndsWithPlaceholder = false; } const Token &getEOFTok() const { return SyntheticEOFToken; } void sawHashOrHashAtBefore(const bool HasLeadingSpace, const bool IsHashAt) { StringifyBefore = !IsHashAt; CharifyBefore = IsHashAt; LeadingSpaceForStringifiedToken = HasLeadingSpace; } void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; } void hasPlaceholderBeforeRParen() { if (isAtTopLevel()) EndsWithPlaceholder = true; } bool beginsWithPlaceholder() const { assert(!isReset() && "Must only be called if the state has not been reset"); return BeginsWithPlaceholder; } bool endsWithPlaceholder() const { assert(!isReset() && "Must only be called if the state has not been reset"); return EndsWithPlaceholder; } bool hasCharifyBefore() const { assert(!isReset() && "Must only be called if the state has not been reset"); return CharifyBefore; } bool hasStringifyOrCharifyBefore() const { return hasStringifyBefore() || hasCharifyBefore(); } unsigned int getNumberOfTokensPriorToVAOpt() const { assert(!isReset() && "Must only be called if the state has not been reset"); return NumOfTokensPriorToVAOpt; } bool getLeadingSpaceForStringifiedToken() const { assert(hasStringifyBefore() && "Must only be called if this has been marked for stringification"); return LeadingSpaceForStringifiedToken; } void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc, const unsigned int NumPriorTokens) { assert(VAOptLoc.isFileID() && "Must not come from a macro expansion"); assert(isReset() && "Must only be called if the state has been reset"); VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(SourceLocation()); this->VAOptLoc = VAOptLoc; NumOfTokensPriorToVAOpt = NumPriorTokens; assert(NumOfTokensPriorToVAOpt > -1 && "Too many prior tokens"); } SourceLocation getVAOptLoc() const { assert(!isReset() && "Must only be called if the state has not been reset"); assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid"); return VAOptLoc; } using VAOptDefinitionContext::isVAOptToken; using VAOptDefinitionContext::isInVAOpt; using VAOptDefinitionContext::sawClosingParen; using VAOptDefinitionContext::sawOpeningParen; }; } // end namespace clang #endif