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(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.
enterScope()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).
exitScope()56     void exitScope() {
57       Ident__VA_ARGS__->setIsPoisoned(true);
58       Ident__VA_OPT__->setIsPoisoned(true);
59     }
60 
~VariadicMacroScopeGuard()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:
VAOptDefinitionContext(Preprocessor & PP)74     VAOptDefinitionContext(Preprocessor &PP)
75         : Ident__VA_OPT__(PP.Ident__VA_OPT__) {}
76 
isVAOptToken(const Token & T)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 ')'.
isInVAOpt()83     bool isInVAOpt() const { return UnmatchedOpeningParens.size(); }
84 
85     /// Call this function as soon as you see __VA_OPT__ and '('.
sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc)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 
getUnmatchedOpeningParenLoc()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.
sawClosingParen()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.
sawOpeningParen(SourceLocation LParenLoc)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__?
isAtTopLevel()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     LLVM_PREFERRED_TYPE(bool)
133     unsigned LeadingSpaceForStringifiedToken : 1;
134 
135     LLVM_PREFERRED_TYPE(bool)
136     unsigned StringifyBefore : 1;
137     LLVM_PREFERRED_TYPE(bool)
138     unsigned CharifyBefore : 1;
139     LLVM_PREFERRED_TYPE(bool)
140     unsigned BeginsWithPlaceholder : 1;
141     LLVM_PREFERRED_TYPE(bool)
142     unsigned EndsWithPlaceholder : 1;
143 
hasStringifyBefore()144     bool hasStringifyBefore() const {
145       assert(!isReset() &&
146              "Must only be called if the state has not been reset");
147       return StringifyBefore;
148     }
149 
isReset()150     bool isReset() const {
151       return NumOfTokensPriorToVAOpt == -1 ||
152              VAOptLoc.isInvalid();
153     }
154 
155   public:
VAOptExpansionContext(Preprocessor & PP)156     VAOptExpansionContext(Preprocessor &PP)
157         : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false),
158           StringifyBefore(false), CharifyBefore(false),
159           BeginsWithPlaceholder(false), EndsWithPlaceholder(false) {
160       SyntheticEOFToken.startToken();
161       SyntheticEOFToken.setKind(tok::eof);
162     }
163 
reset()164     void reset() {
165       VAOptLoc = SourceLocation();
166       NumOfTokensPriorToVAOpt = -1;
167       LeadingSpaceForStringifiedToken = false;
168       StringifyBefore = false;
169       CharifyBefore = false;
170       BeginsWithPlaceholder = false;
171       EndsWithPlaceholder = false;
172     }
173 
getEOFTok()174     const Token &getEOFTok() const { return SyntheticEOFToken; }
175 
sawHashOrHashAtBefore(const bool HasLeadingSpace,const bool IsHashAt)176     void sawHashOrHashAtBefore(const bool HasLeadingSpace,
177                                const bool IsHashAt) {
178 
179       StringifyBefore = !IsHashAt;
180       CharifyBefore = IsHashAt;
181       LeadingSpaceForStringifiedToken = HasLeadingSpace;
182     }
183 
hasPlaceholderAfterHashhashAtStart()184     void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; }
hasPlaceholderBeforeRParen()185     void hasPlaceholderBeforeRParen() {
186       if (isAtTopLevel())
187         EndsWithPlaceholder = true;
188     }
189 
190 
beginsWithPlaceholder()191     bool beginsWithPlaceholder() const {
192       assert(!isReset() &&
193              "Must only be called if the state has not been reset");
194       return BeginsWithPlaceholder;
195     }
endsWithPlaceholder()196     bool endsWithPlaceholder() const {
197       assert(!isReset() &&
198              "Must only be called if the state has not been reset");
199       return EndsWithPlaceholder;
200     }
201 
hasCharifyBefore()202     bool hasCharifyBefore() const {
203       assert(!isReset() &&
204              "Must only be called if the state has not been reset");
205       return CharifyBefore;
206     }
hasStringifyOrCharifyBefore()207     bool hasStringifyOrCharifyBefore() const {
208       return hasStringifyBefore() || hasCharifyBefore();
209     }
210 
getNumberOfTokensPriorToVAOpt()211     unsigned int getNumberOfTokensPriorToVAOpt() const {
212       assert(!isReset() &&
213              "Must only be called if the state has not been reset");
214       return NumOfTokensPriorToVAOpt;
215     }
216 
getLeadingSpaceForStringifiedToken()217     bool getLeadingSpaceForStringifiedToken() const {
218       assert(hasStringifyBefore() &&
219              "Must only be called if this has been marked for stringification");
220       return LeadingSpaceForStringifiedToken;
221     }
222 
sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc,const unsigned int NumPriorTokens)223     void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc,
224                                          const unsigned int NumPriorTokens) {
225       assert(VAOptLoc.isFileID() && "Must not come from a macro expansion");
226       assert(isReset() && "Must only be called if the state has been reset");
227       VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(SourceLocation());
228       this->VAOptLoc = VAOptLoc;
229       NumOfTokensPriorToVAOpt = NumPriorTokens;
230       assert(NumOfTokensPriorToVAOpt > -1 &&
231              "Too many prior tokens");
232     }
233 
getVAOptLoc()234     SourceLocation getVAOptLoc() const {
235       assert(!isReset() &&
236              "Must only be called if the state has not been reset");
237       assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid");
238       return VAOptLoc;
239     }
240     using VAOptDefinitionContext::isVAOptToken;
241     using VAOptDefinitionContext::isInVAOpt;
242     using VAOptDefinitionContext::sawClosingParen;
243     using VAOptDefinitionContext::sawOpeningParen;
244 
245   };
246 }  // end namespace clang
247 
248 #endif
249