Coverage Report

Created: 2020-09-15 12:33

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