Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/include/clang/Basic/PartialDiagnostic.h
Line
Count
Source (jump to first uncovered line)
1
//===- PartialDiagnostic.h - Diagnostic "closures" --------------*- 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
/// \file
10
/// Implements a partial diagnostic that can be emitted anwyhere
11
/// in a DiagnosticBuilder stream.
12
//
13
//===----------------------------------------------------------------------===//
14
15
#ifndef LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
16
#define LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
17
18
#include "clang/Basic/Diagnostic.h"
19
#include "clang/Basic/PartialDiagnostic.h"
20
#include "clang/Basic/LLVM.h"
21
#include "clang/Basic/SourceLocation.h"
22
#include "llvm/ADT/SmallVector.h"
23
#include "llvm/ADT/StringRef.h"
24
#include <cassert>
25
#include <cstdint>
26
#include <string>
27
#include <type_traits>
28
#include <utility>
29
30
namespace clang {
31
32
class DeclContext;
33
class IdentifierInfo;
34
35
class PartialDiagnostic {
36
public:
37
  enum {
38
      // The MaxArguments and MaxFixItHints member enum values from
39
      // DiagnosticsEngine are private but DiagnosticsEngine declares
40
      // PartialDiagnostic a friend.  These enum values are redeclared
41
      // here so that the nested Storage class below can access them.
42
      MaxArguments = DiagnosticsEngine::MaxArguments
43
  };
44
45
  struct Storage {
46
    enum {
47
        /// The maximum number of arguments we can hold. We
48
        /// currently only support up to 10 arguments (%0-%9).
49
        ///
50
        /// A single diagnostic with more than that almost certainly has to
51
        /// be simplified anyway.
52
        MaxArguments = PartialDiagnostic::MaxArguments
53
    };
54
55
    /// The number of entries in Arguments.
56
    unsigned char NumDiagArgs = 0;
57
58
    /// Specifies for each argument whether it is in DiagArgumentsStr
59
    /// or in DiagArguments.
60
    unsigned char DiagArgumentsKind[MaxArguments];
61
62
    /// The values for the various substitution positions.
63
    ///
64
    /// This is used when the argument is not an std::string. The specific value
65
    /// is mangled into an intptr_t and the interpretation depends on exactly
66
    /// what sort of argument kind it is.
67
    intptr_t DiagArgumentsVal[MaxArguments];
68
69
    /// The values for the various substitution positions that have
70
    /// string arguments.
71
    std::string DiagArgumentsStr[MaxArguments];
72
73
    /// The list of ranges added to this diagnostic.
74
    SmallVector<CharSourceRange, 8> DiagRanges;
75
76
    /// If valid, provides a hint with some code to insert, remove, or
77
    /// modify at a particular position.
78
    SmallVector<FixItHint, 6>  FixItHints;
79
80
694k
    Storage() = default;
81
  };
82
83
  /// An allocator for Storage objects, which uses a small cache to
84
  /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
85
  class StorageAllocator {
86
    static const unsigned NumCached = 16;
87
    Storage Cached[NumCached];
88
    Storage *FreeList[NumCached];
89
    unsigned NumFreeListEntries;
90
91
  public:
92
    StorageAllocator();
93
    ~StorageAllocator();
94
95
    /// Allocate new storage.
96
1.87M
    Storage *Allocate() {
97
1.87M
      if (NumFreeListEntries == 0)
98
26.8k
        return new Storage;
99
1.84M
100
1.84M
      Storage *Result = FreeList[--NumFreeListEntries];
101
1.84M
      Result->NumDiagArgs = 0;
102
1.84M
      Result->DiagRanges.clear();
103
1.84M
      Result->FixItHints.clear();
104
1.84M
      return Result;
105
1.84M
    }
106
107
    /// Free the given storage object.
108
1.86M
    void Deallocate(Storage *S) {
109
1.86M
      if (S >= Cached && 
S <= Cached + NumCached1.85M
) {
110
1.84M
        FreeList[NumFreeListEntries++] = S;
111
1.84M
        return;
112
1.84M
      }
113
17.4k
114
17.4k
      delete S;
115
17.4k
    }
116
  };
117
118
private:
119
  // NOTE: Sema assumes that PartialDiagnostic is location-invariant
120
  // in the sense that its bits can be safely memcpy'ed and destructed
121
  // in the new location.
122
123
  /// The diagnostic ID.
124
  mutable unsigned DiagID = 0;
125
126
  /// Storage for args and ranges.
127
  mutable Storage *DiagStorage = nullptr;
128
129
  /// Allocator used to allocate storage for this diagnostic.
130
  StorageAllocator *Allocator = nullptr;
131
132
  /// Retrieve storage for this particular diagnostic.
133
1.87M
  Storage *getStorage() const {
134
1.87M
    if (DiagStorage)
135
0
      return DiagStorage;
136
1.87M
137
1.87M
    if (Allocator)
138
1.87M
      DiagStorage = Allocator->Allocate();
139
0
    else {
140
0
      assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
141
0
      DiagStorage = new Storage;
142
0
    }
143
1.87M
    return DiagStorage;
144
1.87M
  }
145
146
12.8M
  void freeStorage() {
147
12.8M
    if (!DiagStorage)
148
10.9M
      return;
149
1.86M
150
1.86M
    // The hot path for PartialDiagnostic is when we just used it to wrap an ID
151
1.86M
    // (typically so we have the flexibility of passing a more complex
152
1.86M
    // diagnostic into the callee, but that does not commonly occur).
153
1.86M
    //
154
1.86M
    // Split this out into a slow function for silly compilers (*cough*) which
155
1.86M
    // can't do decent partial inlining.
156
1.86M
    freeStorageSlow();
157
1.86M
  }
158
159
1.86M
  void freeStorageSlow() {
160
1.86M
    if (Allocator)
161
1.86M
      Allocator->Deallocate(DiagStorage);
162
0
    else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
163
0
      delete DiagStorage;
164
1.86M
    DiagStorage = nullptr;
165
1.86M
  }
166
167
358k
  void AddSourceRange(const CharSourceRange &R) const {
168
358k
    if (!DiagStorage)
169
51.0k
      DiagStorage = getStorage();
170
358k
171
358k
    DiagStorage->DiagRanges.push_back(R);
172
358k
  }
173
174
17.9k
  void AddFixItHint(const FixItHint &Hint) const {
175
17.9k
    if (Hint.isNull())
176
17.7k
      return;
177
254
178
254
    if (!DiagStorage)
179
109
      DiagStorage = getStorage();
180
254
181
254
    DiagStorage->FixItHints.push_back(Hint);
182
254
  }
183
184
public:
185
  struct NullDiagnostic {};
186
187
  /// Create a null partial diagnostic, which cannot carry a payload,
188
  /// and only exists to be swapped with a real partial diagnostic.
189
147k
  PartialDiagnostic(NullDiagnostic) {}
190
191
  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
192
5.38M
      : DiagID(DiagID), Allocator(&Allocator) {}
193
194
  PartialDiagnostic(const PartialDiagnostic &Other)
195
3.07M
      : DiagID(Other.DiagID), Allocator(Other.Allocator) {
196
3.07M
    if (Other.DiagStorage) {
197
82.1k
      DiagStorage = getStorage();
198
82.1k
      *DiagStorage = *Other.DiagStorage;
199
82.1k
    }
200
3.07M
  }
201
202
  PartialDiagnostic(PartialDiagnostic &&Other)
203
      : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
204
2.05M
        Allocator(Other.Allocator) {
205
2.05M
    Other.DiagStorage = nullptr;
206
2.05M
  }
207
208
  PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
209
      : DiagID(Other.DiagID), DiagStorage(DiagStorage),
210
73
        Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) {
211
73
    if (Other.DiagStorage)
212
2
      *this->DiagStorage = *Other.DiagStorage;
213
73
  }
214
215
  PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
216
159k
      : DiagID(Other.getID()), Allocator(&Allocator) {
217
159k
    // Copy arguments.
218
347k
    for (unsigned I = 0, N = Other.getNumArgs(); I != N; 
++I188k
) {
219
188k
      if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
220
113k
        AddString(Other.getArgStdStr(I));
221
74.9k
      else
222
74.9k
        AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
223
188k
    }
224
159k
225
159k
    // Copy source ranges.
226
312k
    for (unsigned I = 0, N = Other.getNumRanges(); I != N; 
++I152k
)
227
152k
      AddSourceRange(Other.getRange(I));
228
159k
229
159k
    // Copy fix-its.
230
159k
    for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; 
++I45
)
231
45
      AddFixItHint(Other.getFixItHint(I));
232
159k
  }
233
234
110k
  PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
235
110k
    DiagID = Other.DiagID;
236
110k
    if (Other.DiagStorage) {
237
15.7k
      if (!DiagStorage)
238
15.7k
        DiagStorage = getStorage();
239
15.7k
240
15.7k
      *DiagStorage = *Other.DiagStorage;
241
95.0k
    } else {
242
95.0k
      freeStorage();
243
95.0k
    }
244
110k
245
110k
    return *this;
246
110k
  }
247
248
14.2k
  PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
249
14.2k
    freeStorage();
250
14.2k
251
14.2k
    DiagID = Other.DiagID;
252
14.2k
    DiagStorage = Other.DiagStorage;
253
14.2k
    Allocator = Other.Allocator;
254
14.2k
255
14.2k
    Other.DiagStorage = nullptr;
256
14.2k
    return *this;
257
14.2k
  }
258
259
10.8M
  ~PartialDiagnostic() {
260
10.8M
    freeStorage();
261
10.8M
  }
262
263
147k
  void swap(PartialDiagnostic &PD) {
264
147k
    std::swap(DiagID, PD.DiagID);
265
147k
    std::swap(DiagStorage, PD.DiagStorage);
266
147k
    std::swap(Allocator, PD.Allocator);
267
147k
  }
268
269
106k
  unsigned getDiagID() const { return DiagID; }
270
271
2.02M
  void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
272
2.02M
    if (!DiagStorage)
273
1.59M
      DiagStorage = getStorage();
274
2.02M
275
2.02M
    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
276
2.02M
           "Too many arguments to diagnostic!");
277
2.02M
    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
278
2.02M
    DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
279
2.02M
  }
280
281
152k
  void AddString(StringRef V) const {
282
152k
    if (!DiagStorage)
283
127k
      DiagStorage = getStorage();
284
152k
285
152k
    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
286
152k
           "Too many arguments to diagnostic!");
287
152k
    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
288
152k
      = DiagnosticsEngine::ak_std_string;
289
152k
    DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
290
152k
  }
291
292
92.8k
  void Emit(const DiagnosticBuilder &DB) const {
293
92.8k
    if (!DiagStorage)
294
5.15k
      return;
295
87.7k
296
87.7k
    // Add all arguments.
297
321k
    
for (unsigned i = 0, e = DiagStorage->NumDiagArgs; 87.7k
i != e;
++i233k
) {
298
233k
      if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
299
233k
            == DiagnosticsEngine::ak_std_string)
300
19.5k
        DB.AddString(DiagStorage->DiagArgumentsStr[i]);
301
214k
      else
302
214k
        DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
303
214k
            (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
304
233k
    }
305
87.7k
306
87.7k
    // Add all ranges.
307
87.7k
    for (const CharSourceRange &Range : DiagStorage->DiagRanges)
308
119k
      DB.AddSourceRange(Range);
309
87.7k
310
87.7k
    // Add all fix-its.
311
87.7k
    for (const FixItHint &Fix : DiagStorage->FixItHints)
312
214
      DB.AddFixItHint(Fix);
313
87.7k
  }
314
315
  void EmitToString(DiagnosticsEngine &Diags,
316
231
                    SmallVectorImpl<char> &Buf) const {
317
231
    // FIXME: It should be possible to render a diagnostic to a string without
318
231
    //        messing with the state of the diagnostics engine.
319
231
    DiagnosticBuilder DB(Diags.Report(getDiagID()));
320
231
    Emit(DB);
321
231
    DB.FlushCounts();
322
231
    Diagnostic(&Diags).FormatDiagnostic(Buf);
323
231
    DB.Clear();
324
231
    Diags.Clear();
325
231
  }
326
327
  /// Clear out this partial diagnostic, giving it a new diagnostic ID
328
  /// and removing all of its arguments, ranges, and fix-it hints.
329
1.88M
  void Reset(unsigned DiagID = 0) {
330
1.88M
    this->DiagID = DiagID;
331
1.88M
    freeStorage();
332
1.88M
  }
333
334
73
  bool hasStorage() const { return DiagStorage != nullptr; }
335
336
  /// Retrieve the string argument at the given index.
337
16
  StringRef getStringArg(unsigned I) {
338
16
    assert(DiagStorage && "No diagnostic storage?");
339
16
    assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
340
16
    assert(DiagStorage->DiagArgumentsKind[I]
341
16
             == DiagnosticsEngine::ak_std_string && "Not a string arg");
342
16
    return DiagStorage->DiagArgumentsStr[I];
343
16
  }
344
345
  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
346
44.4k
                                             unsigned I) {
347
44.4k
    PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
348
44.4k
    return PD;
349
44.4k
  }
350
351
  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
352
354k
                                             int I) {
353
354k
    PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
354
354k
    return PD;
355
354k
  }
356
357
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
358
1.46k
                                                    const char *S) {
359
1.46k
    PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
360
1.46k
                    DiagnosticsEngine::ak_c_string);
361
1.46k
    return PD;
362
1.46k
  }
363
364
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
365
39.0k
                                                    StringRef S) {
366
39.0k
367
39.0k
    PD.AddString(S);
368
39.0k
    return PD;
369
39.0k
  }
370
371
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
372
405
                                                    const IdentifierInfo *II) {
373
405
    PD.AddTaggedVal(reinterpret_cast<intptr_t>(II),
374
405
                    DiagnosticsEngine::ak_identifierinfo);
375
405
    return PD;
376
405
  }
377
378
  // Adds a DeclContext to the diagnostic. The enable_if template magic is here
379
  // so that we only match those arguments that are (statically) DeclContexts;
380
  // other arguments that derive from DeclContext (e.g., RecordDecls) will not
381
  // match.
382
  template<typename T>
383
  friend inline
384
  typename std::enable_if<std::is_same<T, DeclContext>::value,
385
                          const PartialDiagnostic &>::type
386
268
  operator<<(const PartialDiagnostic &PD, T *DC) {
387
268
    PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
388
268
                    DiagnosticsEngine::ak_declcontext);
389
268
    return PD;
390
268
  }
391
392
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
393
205k
                                                    SourceRange R) {
394
205k
    PD.AddSourceRange(CharSourceRange::getTokenRange(R));
395
205k
    return PD;
396
205k
  }
397
398
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
399
331
                                                    const CharSourceRange &R) {
400
331
    PD.AddSourceRange(R);
401
331
    return PD;
402
331
  }
403
404
  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
405
17.9k
                                             const FixItHint &Hint) {
406
17.9k
    PD.AddFixItHint(Hint);
407
17.9k
    return PD;
408
17.9k
  }
409
};
410
411
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
412
0
                                           const PartialDiagnostic &PD) {
413
0
  PD.Emit(DB);
414
0
  return DB;
415
0
}
416
417
/// A partial diagnostic along with the source location where this
418
/// diagnostic occurs.
419
using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
420
421
} // namespace clang
422
423
#endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H