summaryrefslogtreecommitdiffstats
path: root/include/clang/Lex/VariadicMacroSupport.h
blob: 989e0ac703c9b7cb674ff3939d4b4dbad0e70989 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//===- VariadicMacroSupport.h - state machines and scope guards -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines support types to help with preprocessing variadic macro
// (i.e. macros that use: ellipses __VA_ARGS__ ) definitions and
// expansions.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H
#define LLVM_CLANG_LEX_VARIADICMACROSUPPORT_H

#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/SmallVector.h"

namespace clang {
  class Preprocessor;

  /// An RAII class that tracks when the Preprocessor starts and stops lexing
  /// the definition of a (ISO C/C++) variadic macro.  As an example, this is
  /// useful for unpoisoning and repoisoning certain identifiers (such as
  /// __VA_ARGS__) that are only allowed in this context.  Also, being a friend
  /// of the Preprocessor class allows it to access PP's cached identifiers
  /// directly (as opposed to performing a lookup each time).
  class VariadicMacroScopeGuard {
    const Preprocessor &PP;
    IdentifierInfo *const Ident__VA_ARGS__;
    IdentifierInfo *const Ident__VA_OPT__;

  public:
    VariadicMacroScopeGuard(const Preprocessor &P)
        : PP(P), Ident__VA_ARGS__(PP.Ident__VA_ARGS__),
          Ident__VA_OPT__(PP.Ident__VA_OPT__) {
      assert(Ident__VA_ARGS__->isPoisoned() && "__VA_ARGS__ should be poisoned "
                                              "outside an ISO C/C++ variadic "
                                              "macro definition!");
      assert(
          !Ident__VA_OPT__ ||
          (Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!"));
    }

    /// Client code should call this function just before the Preprocessor is
    /// about to Lex tokens from the definition of a variadic (ISO C/C++) macro.
    void enterScope() {
      Ident__VA_ARGS__->setIsPoisoned(false);
      if (Ident__VA_OPT__)
        Ident__VA_OPT__->setIsPoisoned(false);
    }

    /// Client code should call this function as soon as the Preprocessor has
    /// either completed lexing the macro's definition tokens, or an error
    /// occurred and the context is being exited.  This function is idempotent
    /// (might be explicitly called, and then reinvoked via the destructor).
    void exitScope() {
      Ident__VA_ARGS__->setIsPoisoned(true);
      if (Ident__VA_OPT__)
        Ident__VA_OPT__->setIsPoisoned(true);
    }

    ~VariadicMacroScopeGuard() { exitScope(); }
  };

  /// A class for tracking whether we're inside a VA_OPT during a
  /// traversal of the tokens of a variadic macro definition.
  class VAOptDefinitionContext {
    /// Contains all the locations of so far unmatched lparens.
    SmallVector<SourceLocation, 8> UnmatchedOpeningParens;

    const IdentifierInfo *const Ident__VA_OPT__;


  public:
    VAOptDefinitionContext(Preprocessor &PP)
        : Ident__VA_OPT__(PP.Ident__VA_OPT__) {}

    bool isVAOptToken(const Token &T) const {
      return Ident__VA_OPT__ && T.getIdentifierInfo() == Ident__VA_OPT__;
    }

    /// Returns true if we have seen the __VA_OPT__ and '(' but before having
    /// seen the matching ')'.
    bool isInVAOpt() const { return UnmatchedOpeningParens.size(); }

    /// Call this function as soon as you see __VA_OPT__ and '('.
    void sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc) {
      assert(!isInVAOpt() && "Must NOT be within VAOPT context to call this");
      UnmatchedOpeningParens.push_back(LParenLoc);

    }

    SourceLocation getUnmatchedOpeningParenLoc() const {
      assert(isInVAOpt() && "Must be within VAOPT context to call this");
      return UnmatchedOpeningParens.back();
    }

    /// Call this function each time an rparen is seen.  It returns true only if
    /// the rparen that was just seen was the eventual (non-nested) closing
    /// paren for VAOPT, and ejects us out of the VAOPT context.
    bool sawClosingParen() {
      assert(isInVAOpt() && "Must be within VAOPT context to call this");
      UnmatchedOpeningParens.pop_back();
      return !UnmatchedOpeningParens.size();
    }

    /// Call this function each time an lparen is seen.
    void sawOpeningParen(SourceLocation LParenLoc) {
      assert(isInVAOpt() && "Must be within VAOPT context to call this");
      UnmatchedOpeningParens.push_back(LParenLoc);
    }

    /// Are we at the top level within the __VA_OPT__?
    bool isAtTopLevel() const { return UnmatchedOpeningParens.size() == 1; }
  };

  /// A class for tracking whether we're inside a VA_OPT during a
  /// traversal of the tokens of a macro during macro expansion.
  class VAOptExpansionContext : VAOptDefinitionContext {

    Token SyntheticEOFToken;

    // The (spelling) location of the current __VA_OPT__ in the replacement list
    // of the function-like macro being expanded.
    SourceLocation VAOptLoc;

    // NumOfTokensPriorToVAOpt : when != -1, contains the index *of* the first
    // token of the current VAOPT contents (so we know where to start eager
    // token-pasting and stringification) *within*  the substituted tokens of
    // the function-like macro's new replacement list.
    int NumOfTokensPriorToVAOpt = -1;

    unsigned LeadingSpaceForStringifiedToken : 1;

    unsigned StringifyBefore : 1;
    unsigned CharifyBefore : 1;
    unsigned BeginsWithPlaceholder : 1;
    unsigned EndsWithPlaceholder : 1;

    bool hasStringifyBefore() const {
      assert(!isReset() &&
             "Must only be called if the state has not been reset");
      return StringifyBefore;
    }

    bool isReset() const {
      return NumOfTokensPriorToVAOpt == -1 ||
             VAOptLoc.isInvalid();
    }

  public:
    VAOptExpansionContext(Preprocessor &PP)
        : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false),
          StringifyBefore(false), CharifyBefore(false),
          BeginsWithPlaceholder(false), EndsWithPlaceholder(false) {
      SyntheticEOFToken.startToken();
      SyntheticEOFToken.setKind(tok::eof);
    }

    void reset() {
      VAOptLoc = SourceLocation();
      NumOfTokensPriorToVAOpt = -1;
      LeadingSpaceForStringifiedToken = false;
      StringifyBefore = false;
      CharifyBefore = false;
      BeginsWithPlaceholder = false;
      EndsWithPlaceholder = false;
    }

    const Token &getEOFTok() const { return SyntheticEOFToken; }

    void sawHashOrHashAtBefore(const bool HasLeadingSpace,
                               const bool IsHashAt) {

      StringifyBefore = !IsHashAt;
      CharifyBefore = IsHashAt;
      LeadingSpaceForStringifiedToken = HasLeadingSpace;
    }

    void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; }
    void hasPlaceholderBeforeRParen() {
      if (isAtTopLevel())
        EndsWithPlaceholder = true;
    }


    bool beginsWithPlaceholder() const {
      assert(!isReset() &&
             "Must only be called if the state has not been reset");
      return BeginsWithPlaceholder;
    }
    bool endsWithPlaceholder() const {
      assert(!isReset() &&
             "Must only be called if the state has not been reset");
      return EndsWithPlaceholder;
    }

    bool hasCharifyBefore() const {
      assert(!isReset() &&
             "Must only be called if the state has not been reset");
      return CharifyBefore;
    }
    bool hasStringifyOrCharifyBefore() const {
      return hasStringifyBefore() || hasCharifyBefore();
    }

    unsigned int getNumberOfTokensPriorToVAOpt() const {
      assert(!isReset() &&
             "Must only be called if the state has not been reset");
      return NumOfTokensPriorToVAOpt;
    }

    bool getLeadingSpaceForStringifiedToken() const {
      assert(hasStringifyBefore() &&
             "Must only be called if this has been marked for stringification");
      return LeadingSpaceForStringifiedToken;
    }

    void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc,
                                         const unsigned int NumPriorTokens) {
      assert(VAOptLoc.isFileID() && "Must not come from a macro expansion");
      assert(isReset() && "Must only be called if the state has been reset");
      VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(SourceLocation());
      this->VAOptLoc = VAOptLoc;
      NumOfTokensPriorToVAOpt = NumPriorTokens;
      assert(NumOfTokensPriorToVAOpt > -1 &&
             "Too many prior tokens");
    }

    SourceLocation getVAOptLoc() const {
      assert(!isReset() &&
             "Must only be called if the state has not been reset");
      assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid");
      return VAOptLoc;
    }
    using VAOptDefinitionContext::isVAOptToken;
    using VAOptDefinitionContext::isInVAOpt;
    using VAOptDefinitionContext::sawClosingParen;
    using VAOptDefinitionContext::sawOpeningParen;

  };
}  // end namespace clang

#endif