Coverage Report

Created: 2024-01-17 10:31

/src/llvm-project/clang/include/clang/Lex/VariadicMacroSupport.h
Line
Count
Source (jump to first uncovered line)
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
18.4k
          Ident__VA_OPT__(PP.Ident__VA_OPT__) {
39
18.4k
      assert(Ident__VA_ARGS__->isPoisoned() && "__VA_ARGS__ should be poisoned "
40
18.4k
                                              "outside an ISO C/C++ variadic "
41
18.4k
                                              "macro definition!");
42
0
      assert(Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!");
43
18.4k
    }
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
0
    void enterScope() {
48
0
      Ident__VA_ARGS__->setIsPoisoned(false);
49
0
      Ident__VA_OPT__->setIsPoisoned(false);
50
0
    }
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
18.4k
    void exitScope() {
57
18.4k
      Ident__VA_ARGS__->setIsPoisoned(true);
58
18.4k
      Ident__VA_OPT__->setIsPoisoned(true);
59
18.4k
    }
60
61
18.4k
    ~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
23
        : Ident__VA_OPT__(PP.Ident__VA_OPT__) {}
76
77
207
    bool isVAOptToken(const Token &T) const {
78
207
      return Ident__VA_OPT__ && T.getIdentifierInfo() == Ident__VA_OPT__;
79
207
    }
80
81
    /// Returns true if we have seen the __VA_OPT__ and '(' but before having
82
    /// seen the matching ')'.
83
230
    bool isInVAOpt() const { return UnmatchedOpeningParens.size(); }
84
85
    /// Call this function as soon as you see __VA_OPT__ and '('.
86
0
    void sawVAOptFollowedByOpeningParens(const SourceLocation LParenLoc) {
87
0
      assert(!isInVAOpt() && "Must NOT be within VAOPT context to call this");
88
0
      UnmatchedOpeningParens.push_back(LParenLoc);
89
90
0
    }
91
92
0
    SourceLocation getUnmatchedOpeningParenLoc() const {
93
0
      assert(isInVAOpt() && "Must be within VAOPT context to call this");
94
0
      return UnmatchedOpeningParens.back();
95
0
    }
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
0
    bool sawClosingParen() {
101
0
      assert(isInVAOpt() && "Must be within VAOPT context to call this");
102
0
      UnmatchedOpeningParens.pop_back();
103
0
      return !UnmatchedOpeningParens.size();
104
0
    }
105
106
    /// Call this function each time an lparen is seen.
107
0
    void sawOpeningParen(SourceLocation LParenLoc) {
108
0
      assert(isInVAOpt() && "Must be within VAOPT context to call this");
109
0
      UnmatchedOpeningParens.push_back(LParenLoc);
110
0
    }
111
112
    /// Are we at the top level within the __VA_OPT__?
113
0
    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
144
0
    bool hasStringifyBefore() const {
145
0
      assert(!isReset() &&
146
0
             "Must only be called if the state has not been reset");
147
0
      return StringifyBefore;
148
0
    }
149
150
0
    bool isReset() const {
151
0
      return NumOfTokensPriorToVAOpt == -1 ||
152
0
             VAOptLoc.isInvalid();
153
0
    }
154
155
  public:
156
    VAOptExpansionContext(Preprocessor &PP)
157
        : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false),
158
          StringifyBefore(false), CharifyBefore(false),
159
0
          BeginsWithPlaceholder(false), EndsWithPlaceholder(false) {
160
0
      SyntheticEOFToken.startToken();
161
0
      SyntheticEOFToken.setKind(tok::eof);
162
0
    }
163
164
0
    void reset() {
165
0
      VAOptLoc = SourceLocation();
166
0
      NumOfTokensPriorToVAOpt = -1;
167
0
      LeadingSpaceForStringifiedToken = false;
168
0
      StringifyBefore = false;
169
0
      CharifyBefore = false;
170
0
      BeginsWithPlaceholder = false;
171
0
      EndsWithPlaceholder = false;
172
0
    }
173
174
0
    const Token &getEOFTok() const { return SyntheticEOFToken; }
175
176
    void sawHashOrHashAtBefore(const bool HasLeadingSpace,
177
0
                               const bool IsHashAt) {
178
179
0
      StringifyBefore = !IsHashAt;
180
0
      CharifyBefore = IsHashAt;
181
0
      LeadingSpaceForStringifiedToken = HasLeadingSpace;
182
0
    }
183
184
0
    void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; }
185
0
    void hasPlaceholderBeforeRParen() {
186
0
      if (isAtTopLevel())
187
0
        EndsWithPlaceholder = true;
188
0
    }
189
190
191
0
    bool beginsWithPlaceholder() const {
192
0
      assert(!isReset() &&
193
0
             "Must only be called if the state has not been reset");
194
0
      return BeginsWithPlaceholder;
195
0
    }
196
0
    bool endsWithPlaceholder() const {
197
0
      assert(!isReset() &&
198
0
             "Must only be called if the state has not been reset");
199
0
      return EndsWithPlaceholder;
200
0
    }
201
202
0
    bool hasCharifyBefore() const {
203
0
      assert(!isReset() &&
204
0
             "Must only be called if the state has not been reset");
205
0
      return CharifyBefore;
206
0
    }
207
0
    bool hasStringifyOrCharifyBefore() const {
208
0
      return hasStringifyBefore() || hasCharifyBefore();
209
0
    }
210
211
0
    unsigned int getNumberOfTokensPriorToVAOpt() const {
212
0
      assert(!isReset() &&
213
0
             "Must only be called if the state has not been reset");
214
0
      return NumOfTokensPriorToVAOpt;
215
0
    }
216
217
0
    bool getLeadingSpaceForStringifiedToken() const {
218
0
      assert(hasStringifyBefore() &&
219
0
             "Must only be called if this has been marked for stringification");
220
0
      return LeadingSpaceForStringifiedToken;
221
0
    }
222
223
    void sawVAOptFollowedByOpeningParens(const SourceLocation VAOptLoc,
224
0
                                         const unsigned int NumPriorTokens) {
225
0
      assert(VAOptLoc.isFileID() && "Must not come from a macro expansion");
226
0
      assert(isReset() && "Must only be called if the state has been reset");
227
0
      VAOptDefinitionContext::sawVAOptFollowedByOpeningParens(SourceLocation());
228
0
      this->VAOptLoc = VAOptLoc;
229
0
      NumOfTokensPriorToVAOpt = NumPriorTokens;
230
0
      assert(NumOfTokensPriorToVAOpt > -1 &&
231
0
             "Too many prior tokens");
232
0
    }
233
234
0
    SourceLocation getVAOptLoc() const {
235
0
      assert(!isReset() &&
236
0
             "Must only be called if the state has not been reset");
237
0
      assert(VAOptLoc.isValid() && "__VA_OPT__ location must be valid");
238
0
      return VAOptLoc;
239
0
    }
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