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