Coverage Report

Created: 2018-07-21 08:31

/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
//                     The LLVM Compiler Infrastructure
4
//
5
// This file is distributed under the University of Illinois Open Source
6
// License. See LICENSE.TXT for details.
7
//
8
//===----------------------------------------------------------------------===//
9
//
10
/// \file
11
/// Implements a partial diagnostic that can be emitted anwyhere
12
/// in a DiagnosticBuilder stream.
13
//
14
//===----------------------------------------------------------------------===//
15
16
#ifndef LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
17
#define LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
18
19
#include "clang/Basic/Diagnostic.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
0
  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
612k
    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.65M
    Storage *Allocate() {
97
1.65M
      if (NumFreeListEntries == 0)
98
26.1k
        return new Storage;
99
1.62M
100
1.62M
      Storage *Result = FreeList[--NumFreeListEntries];
101
1.62M
      Result->NumDiagArgs = 0;
102
1.62M
      Result->DiagRanges.clear();
103
1.62M
      Result->FixItHints.clear();
104
1.62M
      return Result;
105
1.62M
    }
106
107
    /// Free the given storage object.
108
1.64M
    void Deallocate(Storage *S) {
109
1.64M
      if (S >= Cached && 
S <= Cached + NumCached1.63M
) {
110
1.62M
        FreeList[NumFreeListEntries++] = S;
111
1.62M
        return;
112
1.62M
      }
113
16.7k
114
16.7k
      delete S;
115
16.7k
    }
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.65M
  Storage *getStorage() const {
134
1.65M
    if (DiagStorage)
135
0
      return DiagStorage;
136
1.65M
137
1.65M
    if (Allocator)
138
1.65M
      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.65M
    return DiagStorage;
144
1.65M
  }
145
146
11.4M
  void freeStorage() {
147
11.4M
    if (!DiagStorage)
148
9.80M
      return;
149
1.64M
150
1.64M
    // The hot path for PartialDiagnostic is when we just used it to wrap an ID
151
1.64M
    // (typically so we have the flexibility of passing a more complex
152
1.64M
    // diagnostic into the callee, but that does not commonly occur).
153
1.64M
    //
154
1.64M
    // Split this out into a slow function for silly compilers (*cough*) which
155
1.64M
    // can't do decent partial inlining.
156
1.64M
    freeStorageSlow();
157
1.64M
  }
158
159
1.64M
  void freeStorageSlow() {
160
1.64M
    if (Allocator)
161
1.64M
      Allocator->Deallocate(DiagStorage);
162
0
    else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
163
0
      delete DiagStorage;
164
1.64M
    DiagStorage = nullptr;
165
1.64M
  }
166
167
275k
  void AddSourceRange(const CharSourceRange &R) const {
168
275k
    if (!DiagStorage)
169
35.6k
      DiagStorage = getStorage();
170
275k
171
275k
    DiagStorage->DiagRanges.push_back(R);
172
275k
  }
173
174
15.5k
  void AddFixItHint(const FixItHint &Hint) const {
175
15.5k
    if (Hint.isNull())
176
15.3k
      return;
177
205
178
205
    if (!DiagStorage)
179
58
      DiagStorage = getStorage();
180
205
181
205
    DiagStorage->FixItHints.push_back(Hint);
182
205
  }
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
92.9k
  PartialDiagnostic(NullDiagnostic) {}
190
191
  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
192
4.75M
      : DiagID(DiagID), Allocator(&Allocator) {}
193
194
  PartialDiagnostic(const PartialDiagnostic &Other)
195
2.84M
      : DiagID(Other.DiagID), Allocator(Other.Allocator) {
196
2.84M
    if (Other.DiagStorage) {
197
75.2k
      DiagStorage = getStorage();
198
75.2k
      *DiagStorage = *Other.DiagStorage;
199
75.2k
    }
200
2.84M
  }
201
202
  PartialDiagnostic(PartialDiagnostic &&Other)
203
      : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
204
1.93M
        Allocator(Other.Allocator) {
205
1.93M
    Other.DiagStorage = nullptr;
206
1.93M
  }
207
208
  PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
209
      : DiagID(Other.DiagID), DiagStorage(DiagStorage),
210
43
        Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) {
211
43
    if (Other.DiagStorage)
212
2
      *this->DiagStorage = *Other.DiagStorage;
213
43
  }
214
215
  PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
216
98.5k
      : DiagID(Other.getID()), Allocator(&Allocator) {
217
98.5k
    // Copy arguments.
218
219k
    for (unsigned I = 0, N = Other.getNumArgs(); I != N; 
++I120k
) {
219
120k
      if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
220
70.2k
        AddString(Other.getArgStdStr(I));
221
50.4k
      else
222
50.4k
        AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
223
120k
    }
224
98.5k
225
98.5k
    // Copy source ranges.
226
194k
    for (unsigned I = 0, N = Other.getNumRanges(); I != N; 
++I95.5k
)
227
95.5k
      AddSourceRange(Other.getRange(I));
228
98.5k
229
98.5k
    // Copy fix-its.
230
98.6k
    for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; 
++I45
)
231
45
      AddFixItHint(Other.getFixItHint(I));
232
98.5k
  }
233
234
85.3k
  PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
235
85.3k
    DiagID = Other.DiagID;
236
85.3k
    if (Other.DiagStorage) {
237
13.6k
      if (!DiagStorage)
238
13.6k
        DiagStorage = getStorage();
239
13.6k
240
13.6k
      *DiagStorage = *Other.DiagStorage;
241
71.7k
    } else {
242
71.7k
      freeStorage();
243
71.7k
    }
244
85.3k
245
85.3k
    return *this;
246
85.3k
  }
247
248
12.0k
  PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
249
12.0k
    freeStorage();
250
12.0k
251
12.0k
    DiagID = Other.DiagID;
252
12.0k
    DiagStorage = Other.DiagStorage;
253
12.0k
    Allocator = Other.Allocator;
254
12.0k
255
12.0k
    Other.DiagStorage = nullptr;
256
12.0k
    return *this;
257
12.0k
  }
258
259
9.72M
  ~PartialDiagnostic() {
260
9.72M
    freeStorage();
261
9.72M
  }
262
263
92.9k
  void swap(PartialDiagnostic &PD) {
264
92.9k
    std::swap(DiagID, PD.DiagID);
265
92.9k
    std::swap(DiagStorage, PD.DiagStorage);
266
92.9k
    std::swap(Allocator, PD.Allocator);
267
92.9k
  }
268
269
95.4k
  unsigned getDiagID() const { return DiagID; }
270
271
1.81M
  void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
272
1.81M
    if (!DiagStorage)
273
1.44M
      DiagStorage = getStorage();
274
1.81M
275
1.81M
    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
276
1.81M
           "Too many arguments to diagnostic!");
277
1.81M
    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
278
1.81M
    DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
279
1.81M
  }
280
281
106k
  void AddString(StringRef V) const {
282
106k
    if (!DiagStorage)
283
83.4k
      DiagStorage = getStorage();
284
106k
285
106k
    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
286
106k
           "Too many arguments to diagnostic!");
287
106k
    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
288
106k
      = DiagnosticsEngine::ak_std_string;
289
106k
    DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
290
106k
  }
291
292
82.6k
  void Emit(const DiagnosticBuilder &DB) const {
293
82.6k
    if (!DiagStorage)
294
4.84k
      return;
295
77.7k
296
77.7k
    // Add all arguments.
297
290k
    
for (unsigned i = 0, e = DiagStorage->NumDiagArgs; 77.7k
i != e;
++i212k
) {
298
212k
      if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
299
212k
            == DiagnosticsEngine::ak_std_string)
300
17.2k
        DB.AddString(DiagStorage->DiagArgumentsStr[i]);
301
195k
      else
302
195k
        DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
303
195k
            (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
304
212k
    }
305
77.7k
306
77.7k
    // Add all ranges.
307
77.7k
    for (const CharSourceRange &Range : DiagStorage->DiagRanges)
308
109k
      DB.AddSourceRange(Range);
309
77.7k
310
77.7k
    // Add all fix-its.
311
77.7k
    for (const FixItHint &Fix : DiagStorage->FixItHints)
312
165
      DB.AddFixItHint(Fix);
313
77.7k
  }
314
315
  void EmitToString(DiagnosticsEngine &Diags,
316
199
                    SmallVectorImpl<char> &Buf) const {
317
199
    // FIXME: It should be possible to render a diagnostic to a string without
318
199
    //        messing with the state of the diagnostics engine.
319
199
    DiagnosticBuilder DB(Diags.Report(getDiagID()));
320
199
    Emit(DB);
321
199
    DB.FlushCounts();
322
199
    Diagnostic(&Diags).FormatDiagnostic(Buf);
323
199
    DB.Clear();
324
199
    Diags.Clear();
325
199
  }
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.64M
  void Reset(unsigned DiagID = 0) {
330
1.64M
    this->DiagID = DiagID;
331
1.64M
    freeStorage();
332
1.64M
  }
333
334
43
  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
42.2k
                                             unsigned I) {
347
42.2k
    PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
348
42.2k
    return PD;
349
42.2k
  }
350
351
  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
352
330k
                                             int I) {
353
330k
    PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
354
330k
    return PD;
355
330k
  }
356
357
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
358
840
                                                    const char *S) {
359
840
    PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
360
840
                    DiagnosticsEngine::ak_c_string);
361
840
    return PD;
362
840
  }
363
364
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
365
36.1k
                                                    StringRef S) {
366
36.1k
367
36.1k
    PD.AddString(S);
368
36.1k
    return PD;
369
36.1k
  }
370
371
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
372
387
                                                    const IdentifierInfo *II) {
373
387
    PD.AddTaggedVal(reinterpret_cast<intptr_t>(II),
374
387
                    DiagnosticsEngine::ak_identifierinfo);
375
387
    return PD;
376
387
  }
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
179k
                                                    SourceRange R) {
394
179k
    PD.AddSourceRange(CharSourceRange::getTokenRange(R));
395
179k
    return PD;
396
179k
  }
397
398
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
399
323
                                                    const CharSourceRange &R) {
400
323
    PD.AddSourceRange(R);
401
323
    return PD;
402
323
  }
403
404
  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
405
15.4k
                                             const FixItHint &Hint) {
406
15.4k
    PD.AddFixItHint(Hint);
407
15.4k
    return PD;
408
15.4k
  }
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