Coverage Report

Created: 2018-12-09 11:54

/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
632k
    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.74M
    Storage *Allocate() {
97
1.74M
      if (NumFreeListEntries == 0)
98
26.6k
        return new Storage;
99
1.71M
100
1.71M
      Storage *Result = FreeList[--NumFreeListEntries];
101
1.71M
      Result->NumDiagArgs = 0;
102
1.71M
      Result->DiagRanges.clear();
103
1.71M
      Result->FixItHints.clear();
104
1.71M
      return Result;
105
1.71M
    }
106
107
    /// Free the given storage object.
108
1.73M
    void Deallocate(Storage *S) {
109
1.73M
      if (S >= Cached && 
S <= Cached + NumCached1.72M
) {
110
1.71M
        FreeList[NumFreeListEntries++] = S;
111
1.71M
        return;
112
1.71M
      }
113
17.1k
114
17.1k
      delete S;
115
17.1k
    }
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.74M
  Storage *getStorage() const {
134
1.74M
    if (DiagStorage)
135
0
      return DiagStorage;
136
1.74M
137
1.74M
    if (Allocator)
138
1.74M
      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.74M
    return DiagStorage;
144
1.74M
  }
145
146
11.7M
  void freeStorage() {
147
11.7M
    if (!DiagStorage)
148
10.0M
      return;
149
1.73M
150
1.73M
    // The hot path for PartialDiagnostic is when we just used it to wrap an ID
151
1.73M
    // (typically so we have the flexibility of passing a more complex
152
1.73M
    // diagnostic into the callee, but that does not commonly occur).
153
1.73M
    //
154
1.73M
    // Split this out into a slow function for silly compilers (*cough*) which
155
1.73M
    // can't do decent partial inlining.
156
1.73M
    freeStorageSlow();
157
1.73M
  }
158
159
1.73M
  void freeStorageSlow() {
160
1.73M
    if (Allocator)
161
1.73M
      Allocator->Deallocate(DiagStorage);
162
0
    else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
163
0
      delete DiagStorage;
164
1.73M
    DiagStorage = nullptr;
165
1.73M
  }
166
167
315k
  void AddSourceRange(const CharSourceRange &R) const {
168
315k
    if (!DiagStorage)
169
46.7k
      DiagStorage = getStorage();
170
315k
171
315k
    DiagStorage->DiagRanges.push_back(R);
172
315k
  }
173
174
16.3k
  void AddFixItHint(const FixItHint &Hint) const {
175
16.3k
    if (Hint.isNull())
176
16.0k
      return;
177
255
178
255
    if (!DiagStorage)
179
107
      DiagStorage = getStorage();
180
255
181
255
    DiagStorage->FixItHints.push_back(Hint);
182
255
  }
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
118k
  PartialDiagnostic(NullDiagnostic) {}
190
191
  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
192
4.93M
      : 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
76.4k
      DiagStorage = getStorage();
198
76.4k
      *DiagStorage = *Other.DiagStorage;
199
76.4k
    }
200
2.84M
  }
201
202
  PartialDiagnostic(PartialDiagnostic &&Other)
203
      : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
204
1.92M
        Allocator(Other.Allocator) {
205
1.92M
    Other.DiagStorage = nullptr;
206
1.92M
  }
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
129k
      : DiagID(Other.getID()), Allocator(&Allocator) {
217
129k
    // Copy arguments.
218
281k
    for (unsigned I = 0, N = Other.getNumArgs(); I != N; 
++I151k
) {
219
151k
      if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
220
92.0k
        AddString(Other.getArgStdStr(I));
221
59.3k
      else
222
59.3k
        AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
223
151k
    }
224
129k
225
129k
    // Copy source ranges.
226
253k
    for (unsigned I = 0, N = Other.getNumRanges(); I != N; 
++I123k
)
227
123k
      AddSourceRange(Other.getRange(I));
228
129k
229
129k
    // Copy fix-its.
230
129k
    for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; 
++I45
)
231
45
      AddFixItHint(Other.getFixItHint(I));
232
129k
  }
233
234
94.7k
  PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
235
94.7k
    DiagID = Other.DiagID;
236
94.7k
    if (Other.DiagStorage) {
237
14.8k
      if (!DiagStorage)
238
14.8k
        DiagStorage = getStorage();
239
14.8k
240
14.8k
      *DiagStorage = *Other.DiagStorage;
241
79.8k
    } else {
242
79.8k
      freeStorage();
243
79.8k
    }
244
94.7k
245
94.7k
    return *this;
246
94.7k
  }
247
248
13.6k
  PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
249
13.6k
    freeStorage();
250
13.6k
251
13.6k
    DiagID = Other.DiagID;
252
13.6k
    DiagStorage = Other.DiagStorage;
253
13.6k
    Allocator = Other.Allocator;
254
13.6k
255
13.6k
    Other.DiagStorage = nullptr;
256
13.6k
    return *this;
257
13.6k
  }
258
259
9.94M
  ~PartialDiagnostic() {
260
9.94M
    freeStorage();
261
9.94M
  }
262
263
118k
  void swap(PartialDiagnostic &PD) {
264
118k
    std::swap(DiagID, PD.DiagID);
265
118k
    std::swap(DiagStorage, PD.DiagStorage);
266
118k
    std::swap(Allocator, PD.Allocator);
267
118k
  }
268
269
97.8k
  unsigned getDiagID() const { return DiagID; }
270
271
1.90M
  void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
272
1.90M
    if (!DiagStorage)
273
1.49M
      DiagStorage = getStorage();
274
1.90M
275
1.90M
    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
276
1.90M
           "Too many arguments to diagnostic!");
277
1.90M
    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
278
1.90M
    DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
279
1.90M
  }
280
281
129k
  void AddString(StringRef V) const {
282
129k
    if (!DiagStorage)
283
105k
      DiagStorage = getStorage();
284
129k
285
129k
    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
286
129k
           "Too many arguments to diagnostic!");
287
129k
    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
288
129k
      = DiagnosticsEngine::ak_std_string;
289
129k
    DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
290
129k
  }
291
292
84.6k
  void Emit(const DiagnosticBuilder &DB) const {
293
84.6k
    if (!DiagStorage)
294
4.94k
      return;
295
79.6k
296
79.6k
    // Add all arguments.
297
297k
    
for (unsigned i = 0, e = DiagStorage->NumDiagArgs; 79.6k
i != e;
++i217k
) {
298
217k
      if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
299
217k
            == DiagnosticsEngine::ak_std_string)
300
17.9k
        DB.AddString(DiagStorage->DiagArgumentsStr[i]);
301
199k
      else
302
199k
        DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
303
199k
            (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
304
217k
    }
305
79.6k
306
79.6k
    // Add all ranges.
307
79.6k
    for (const CharSourceRange &Range : DiagStorage->DiagRanges)
308
111k
      DB.AddSourceRange(Range);
309
79.6k
310
79.6k
    // Add all fix-its.
311
79.6k
    for (const FixItHint &Fix : DiagStorage->FixItHints)
312
215
      DB.AddFixItHint(Fix);
313
79.6k
  }
314
315
  void EmitToString(DiagnosticsEngine &Diags,
316
201
                    SmallVectorImpl<char> &Buf) const {
317
201
    // FIXME: It should be possible to render a diagnostic to a string without
318
201
    //        messing with the state of the diagnostics engine.
319
201
    DiagnosticBuilder DB(Diags.Report(getDiagID()));
320
201
    Emit(DB);
321
201
    DB.FlushCounts();
322
201
    Diagnostic(&Diags).FormatDiagnostic(Buf);
323
201
    DB.Clear();
324
201
    Diags.Clear();
325
201
  }
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.72M
  void Reset(unsigned DiagID = 0) {
330
1.72M
    this->DiagID = DiagID;
331
1.72M
    freeStorage();
332
1.72M
  }
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.6k
                                             unsigned I) {
347
42.6k
    PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
348
42.6k
    return PD;
349
42.6k
  }
350
351
  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
352
343k
                                             int I) {
353
343k
    PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
354
343k
    return PD;
355
343k
  }
356
357
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
358
1.29k
                                                    const char *S) {
359
1.29k
    PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
360
1.29k
                    DiagnosticsEngine::ak_c_string);
361
1.29k
    return PD;
362
1.29k
  }
363
364
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
365
37.2k
                                                    StringRef S) {
366
37.2k
367
37.2k
    PD.AddString(S);
368
37.2k
    return PD;
369
37.2k
  }
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
191k
                                                    SourceRange R) {
394
191k
    PD.AddSourceRange(CharSourceRange::getTokenRange(R));
395
191k
    return PD;
396
191k
  }
397
398
  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
399
324
                                                    const CharSourceRange &R) {
400
324
    PD.AddSourceRange(R);
401
324
    return PD;
402
324
  }
403
404
  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
405
16.2k
                                             const FixItHint &Hint) {
406
16.2k
    PD.AddFixItHint(Hint);
407
16.2k
    return PD;
408
16.2k
  }
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