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