• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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