Home | History | Annotate | Download | only in Lex
      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