1203955Srdivacky//===--- PartialDiagnostic.h - Diagnostic "closures" ------------*- C++ -*-===//
2198092Srdivacky//
3198092Srdivacky//                     The LLVM Compiler Infrastructure
4198092Srdivacky//
5198092Srdivacky// This file is distributed under the University of Illinois Open Source
6198092Srdivacky// License. See LICENSE.TXT for details.
7198092Srdivacky//
8198092Srdivacky//===----------------------------------------------------------------------===//
9239462Sdim///
10239462Sdim/// \file
11239462Sdim/// \brief Implements a partial diagnostic that can be emitted anwyhere
12239462Sdim/// in a DiagnosticBuilder stream.
13239462Sdim///
14198092Srdivacky//===----------------------------------------------------------------------===//
15198092Srdivacky
16198092Srdivacky#ifndef LLVM_CLANG_PARTIALDIAGNOSTIC_H
17198092Srdivacky#define LLVM_CLANG_PARTIALDIAGNOSTIC_H
18198092Srdivacky
19198092Srdivacky#include "clang/Basic/Diagnostic.h"
20198092Srdivacky#include "clang/Basic/SourceLocation.h"
21198092Srdivacky#include "llvm/ADT/STLExtras.h"
22249423Sdim#include "llvm/Support/Compiler.h"
23218893Sdim#include "llvm/Support/DataTypes.h"
24206084Srdivacky#include <cassert>
25198092Srdivacky
26198092Srdivackynamespace clang {
27198092Srdivacky
28198092Srdivackyclass PartialDiagnostic {
29206084Srdivackypublic:
30234353Sdim  enum {
31234353Sdim      // The MaxArguments and MaxFixItHints member enum values from
32234353Sdim      // DiagnosticsEngine are private but DiagnosticsEngine declares
33234353Sdim      // PartialDiagnostic a friend.  These enum values are redeclared
34234353Sdim      // here so that the nested Storage class below can access them.
35234353Sdim      MaxArguments = DiagnosticsEngine::MaxArguments
36234353Sdim  };
37234353Sdim
38198092Srdivacky  struct Storage {
39234353Sdim    Storage() : NumDiagArgs(0), NumDiagRanges(0) { }
40198092Srdivacky
41198092Srdivacky    enum {
42239462Sdim        /// \brief The maximum number of arguments we can hold. We
43198092Srdivacky        /// currently only support up to 10 arguments (%0-%9).
44239462Sdim        ///
45198092Srdivacky        /// A single diagnostic with more than that almost certainly has to
46198092Srdivacky        /// be simplified anyway.
47234353Sdim        MaxArguments = PartialDiagnostic::MaxArguments
48198092Srdivacky    };
49234353Sdim
50239462Sdim    /// \brief The number of entries in Arguments.
51198092Srdivacky    unsigned char NumDiagArgs;
52234353Sdim
53239462Sdim    /// \brief This is the number of ranges in the DiagRanges array.
54198092Srdivacky    unsigned char NumDiagRanges;
55198092Srdivacky
56239462Sdim    /// \brief Specifies for each argument whether it is in DiagArgumentsStr
57239462Sdim    /// or in DiagArguments.
58198092Srdivacky    unsigned char DiagArgumentsKind[MaxArguments];
59234353Sdim
60239462Sdim    /// \brief The values for the various substitution positions.
61239462Sdim    ///
62234353Sdim    /// This is used when the argument is not an std::string. The specific value
63221345Sdim    /// is mangled into an intptr_t and the interpretation depends on exactly
64198092Srdivacky    /// what sort of argument kind it is.
65200583Srdivacky    intptr_t DiagArgumentsVal[MaxArguments];
66234353Sdim
67218893Sdim    /// \brief The values for the various substitution positions that have
68218893Sdim    /// string arguments.
69218893Sdim    std::string DiagArgumentsStr[MaxArguments];
70234353Sdim
71239462Sdim    /// \brief The list of ranges added to this diagnostic.
72239462Sdim    ///
73239462Sdim    /// It currently only support 10 ranges, could easily be extended if needed.
74210299Sed    CharSourceRange DiagRanges[10];
75234353Sdim
76239462Sdim    /// \brief If valid, provides a hint with some code to insert, remove, or
77239462Sdim    /// modify at a particular position.
78234353Sdim    SmallVector<FixItHint, 6>  FixItHints;
79198092Srdivacky  };
80198092Srdivacky
81234353Sdim  /// \brief An allocator for Storage objects, which uses a small cache to
82206084Srdivacky  /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
83206084Srdivacky  class StorageAllocator {
84219077Sdim    static const unsigned NumCached = 16;
85206084Srdivacky    Storage Cached[NumCached];
86206084Srdivacky    Storage *FreeList[NumCached];
87206084Srdivacky    unsigned NumFreeListEntries;
88234353Sdim
89206084Srdivacky  public:
90206084Srdivacky    StorageAllocator();
91206084Srdivacky    ~StorageAllocator();
92234353Sdim
93206084Srdivacky    /// \brief Allocate new storage.
94206084Srdivacky    Storage *Allocate() {
95206084Srdivacky      if (NumFreeListEntries == 0)
96206084Srdivacky        return new Storage;
97234353Sdim
98206084Srdivacky      Storage *Result = FreeList[--NumFreeListEntries];
99206084Srdivacky      Result->NumDiagArgs = 0;
100206084Srdivacky      Result->NumDiagRanges = 0;
101234353Sdim      Result->FixItHints.clear();
102206084Srdivacky      return Result;
103206084Srdivacky    }
104234353Sdim
105206084Srdivacky    /// \brief Free the given storage object.
106206084Srdivacky    void Deallocate(Storage *S) {
107206084Srdivacky      if (S >= Cached && S <= Cached + NumCached) {
108206084Srdivacky        FreeList[NumFreeListEntries++] = S;
109206084Srdivacky        return;
110206084Srdivacky      }
111234353Sdim
112206084Srdivacky      delete S;
113206084Srdivacky    }
114206084Srdivacky  };
115234353Sdim
116206084Srdivackyprivate:
117205219Srdivacky  // NOTE: Sema assumes that PartialDiagnostic is location-invariant
118205219Srdivacky  // in the sense that its bits can be safely memcpy'ed and destructed
119205219Srdivacky  // in the new location.
120205219Srdivacky
121239462Sdim  /// \brief The diagnostic ID.
122198092Srdivacky  mutable unsigned DiagID;
123234353Sdim
124239462Sdim  /// \brief Storage for args and ranges.
125198092Srdivacky  mutable Storage *DiagStorage;
126198092Srdivacky
127206084Srdivacky  /// \brief Allocator used to allocate storage for this diagnostic.
128206084Srdivacky  StorageAllocator *Allocator;
129234353Sdim
130206084Srdivacky  /// \brief Retrieve storage for this particular diagnostic.
131206084Srdivacky  Storage *getStorage() const {
132206084Srdivacky    if (DiagStorage)
133206084Srdivacky      return DiagStorage;
134234353Sdim
135206084Srdivacky    if (Allocator)
136206084Srdivacky      DiagStorage = Allocator->Allocate();
137206084Srdivacky    else {
138206084Srdivacky      assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
139206084Srdivacky      DiagStorage = new Storage;
140206084Srdivacky    }
141206084Srdivacky    return DiagStorage;
142206084Srdivacky  }
143234353Sdim
144234353Sdim  void freeStorage() {
145198092Srdivacky    if (!DiagStorage)
146206084Srdivacky      return;
147234353Sdim
148234353Sdim    // The hot path for PartialDiagnostic is when we just used it to wrap an ID
149234353Sdim    // (typically so we have the flexibility of passing a more complex
150234353Sdim    // diagnostic into the callee, but that does not commonly occur).
151234353Sdim    //
152234353Sdim    // Split this out into a slow function for silly compilers (*cough*) which
153234353Sdim    // can't do decent partial inlining.
154234353Sdim    freeStorageSlow();
155234353Sdim  }
156234353Sdim
157234353Sdim  void freeStorageSlow() {
158206084Srdivacky    if (Allocator)
159206084Srdivacky      Allocator->Deallocate(DiagStorage);
160206084Srdivacky    else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
161206084Srdivacky      delete DiagStorage;
162206084Srdivacky    DiagStorage = 0;
163198092Srdivacky  }
164234353Sdim
165210299Sed  void AddSourceRange(const CharSourceRange &R) const {
166198092Srdivacky    if (!DiagStorage)
167206084Srdivacky      DiagStorage = getStorage();
168198092Srdivacky
169234353Sdim    assert(DiagStorage->NumDiagRanges <
170198092Srdivacky           llvm::array_lengthof(DiagStorage->DiagRanges) &&
171198092Srdivacky           "Too many arguments to diagnostic!");
172200583Srdivacky    DiagStorage->DiagRanges[DiagStorage->NumDiagRanges++] = R;
173234353Sdim  }
174198092Srdivacky
175206084Srdivacky  void AddFixItHint(const FixItHint &Hint) const {
176202379Srdivacky    if (Hint.isNull())
177202379Srdivacky      return;
178234353Sdim
179202379Srdivacky    if (!DiagStorage)
180206084Srdivacky      DiagStorage = getStorage();
181202379Srdivacky
182234353Sdim    DiagStorage->FixItHints.push_back(Hint);
183202379Srdivacky  }
184234353Sdim
185198092Srdivackypublic:
186239462Sdim  struct NullDiagnostic {};
187239462Sdim  /// \brief Create a null partial diagnostic, which cannot carry a payload,
188239462Sdim  /// and only exists to be swapped with a real partial diagnostic.
189239462Sdim  PartialDiagnostic(NullDiagnostic)
190239462Sdim    : DiagID(0), DiagStorage(0), Allocator(0) { }
191239462Sdim
192206084Srdivacky  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
193206084Srdivacky    : DiagID(DiagID), DiagStorage(0), Allocator(&Allocator) { }
194234353Sdim
195234353Sdim  PartialDiagnostic(const PartialDiagnostic &Other)
196206084Srdivacky    : DiagID(Other.DiagID), DiagStorage(0), Allocator(Other.Allocator)
197200583Srdivacky  {
198206084Srdivacky    if (Other.DiagStorage) {
199206084Srdivacky      DiagStorage = getStorage();
200206084Srdivacky      *DiagStorage = *Other.DiagStorage;
201206084Srdivacky    }
202206084Srdivacky  }
203206084Srdivacky
204249423Sdim#if LLVM_HAS_RVALUE_REFERENCES
205249423Sdim  PartialDiagnostic(PartialDiagnostic &&Other)
206249423Sdim    : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
207249423Sdim      Allocator(Other.Allocator) {
208249423Sdim    Other.DiagStorage = 0;
209249423Sdim  }
210249423Sdim#endif
211249423Sdim
212234353Sdim  PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
213234353Sdim    : DiagID(Other.DiagID), DiagStorage(DiagStorage),
214206084Srdivacky      Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
215206084Srdivacky  {
216200583Srdivacky    if (Other.DiagStorage)
217206084Srdivacky      *this->DiagStorage = *Other.DiagStorage;
218198092Srdivacky  }
219234353Sdim
220226633Sdim  PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
221218893Sdim    : DiagID(Other.getID()), DiagStorage(0), Allocator(&Allocator)
222218893Sdim  {
223218893Sdim    // Copy arguments.
224218893Sdim    for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
225226633Sdim      if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
226218893Sdim        AddString(Other.getArgStdStr(I));
227218893Sdim      else
228218893Sdim        AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
229218893Sdim    }
230234353Sdim
231218893Sdim    // Copy source ranges.
232218893Sdim    for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
233218893Sdim      AddSourceRange(Other.getRange(I));
234234353Sdim
235218893Sdim    // Copy fix-its.
236218893Sdim    for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
237218893Sdim      AddFixItHint(Other.getFixItHint(I));
238218893Sdim  }
239234353Sdim
240200583Srdivacky  PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
241200583Srdivacky    DiagID = Other.DiagID;
242200583Srdivacky    if (Other.DiagStorage) {
243206084Srdivacky      if (!DiagStorage)
244206084Srdivacky        DiagStorage = getStorage();
245234353Sdim
246206084Srdivacky      *DiagStorage = *Other.DiagStorage;
247200583Srdivacky    } else {
248206084Srdivacky      freeStorage();
249200583Srdivacky    }
250200583Srdivacky
251200583Srdivacky    return *this;
252200583Srdivacky  }
253200583Srdivacky
254249423Sdim#if LLVM_HAS_RVALUE_REFERENCES
255249423Sdim  PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
256249423Sdim    freeStorage();
257249423Sdim
258249423Sdim    DiagID = Other.DiagID;
259249423Sdim    DiagStorage = Other.DiagStorage;
260249423Sdim    Allocator = Other.Allocator;
261249423Sdim
262249423Sdim    Other.DiagStorage = 0;
263249423Sdim    return *this;
264249423Sdim  }
265249423Sdim#endif
266249423Sdim
267198092Srdivacky  ~PartialDiagnostic() {
268206084Srdivacky    freeStorage();
269198092Srdivacky  }
270198092Srdivacky
271239462Sdim  void swap(PartialDiagnostic &PD) {
272239462Sdim    std::swap(DiagID, PD.DiagID);
273239462Sdim    std::swap(DiagStorage, PD.DiagStorage);
274239462Sdim    std::swap(Allocator, PD.Allocator);
275239462Sdim  }
276239462Sdim
277198092Srdivacky  unsigned getDiagID() const { return DiagID; }
278198092Srdivacky
279226633Sdim  void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
280206084Srdivacky    if (!DiagStorage)
281206084Srdivacky      DiagStorage = getStorage();
282206084Srdivacky
283206084Srdivacky    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
284206084Srdivacky           "Too many arguments to diagnostic!");
285206084Srdivacky    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
286206084Srdivacky    DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
287206084Srdivacky  }
288206084Srdivacky
289226633Sdim  void AddString(StringRef V) const {
290218893Sdim    if (!DiagStorage)
291218893Sdim      DiagStorage = getStorage();
292234353Sdim
293218893Sdim    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
294218893Sdim           "Too many arguments to diagnostic!");
295218893Sdim    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
296226633Sdim      = DiagnosticsEngine::ak_std_string;
297218893Sdim    DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
298218893Sdim  }
299218893Sdim
300198092Srdivacky  void Emit(const DiagnosticBuilder &DB) const {
301198092Srdivacky    if (!DiagStorage)
302198092Srdivacky      return;
303234353Sdim
304198092Srdivacky    // Add all arguments.
305198092Srdivacky    for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
306226633Sdim      if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
307226633Sdim            == DiagnosticsEngine::ak_std_string)
308218893Sdim        DB.AddString(DiagStorage->DiagArgumentsStr[i]);
309218893Sdim      else
310218893Sdim        DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
311226633Sdim            (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
312198092Srdivacky    }
313234353Sdim
314198092Srdivacky    // Add all ranges.
315198092Srdivacky    for (unsigned i = 0, e = DiagStorage->NumDiagRanges; i != e; ++i)
316200583Srdivacky      DB.AddSourceRange(DiagStorage->DiagRanges[i]);
317234353Sdim
318218893Sdim    // Add all fix-its.
319234353Sdim    for (unsigned i = 0, e = DiagStorage->FixItHints.size(); i != e; ++i)
320206084Srdivacky      DB.AddFixItHint(DiagStorage->FixItHints[i]);
321198092Srdivacky  }
322234353Sdim
323239462Sdim  void EmitToString(DiagnosticsEngine &Diags,
324249423Sdim                    SmallVectorImpl<char> &Buf) const {
325239462Sdim    // FIXME: It should be possible to render a diagnostic to a string without
326239462Sdim    //        messing with the state of the diagnostics engine.
327239462Sdim    DiagnosticBuilder DB(Diags.Report(getDiagID()));
328239462Sdim    Emit(DB);
329239462Sdim    DB.FlushCounts();
330239462Sdim    Diagnostic(&Diags).FormatDiagnostic(Buf);
331239462Sdim    DB.Clear();
332239462Sdim    Diags.Clear();
333239462Sdim  }
334239462Sdim
335206084Srdivacky  /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID
336206084Srdivacky  /// and removing all of its arguments, ranges, and fix-it hints.
337206084Srdivacky  void Reset(unsigned DiagID = 0) {
338206084Srdivacky    this->DiagID = DiagID;
339206084Srdivacky    freeStorage();
340198092Srdivacky  }
341234353Sdim
342206084Srdivacky  bool hasStorage() const { return DiagStorage != 0; }
343234353Sdim
344198092Srdivacky  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
345198092Srdivacky                                             unsigned I) {
346226633Sdim    PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
347198092Srdivacky    return PD;
348198092Srdivacky  }
349198893Srdivacky
350198893Srdivacky  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
351198893Srdivacky                                             int I) {
352226633Sdim    PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
353198893Srdivacky    return PD;
354198893Srdivacky  }
355198893Srdivacky
356198092Srdivacky  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
357198893Srdivacky                                                    const char *S) {
358226633Sdim    PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
359226633Sdim                    DiagnosticsEngine::ak_c_string);
360198893Srdivacky    return PD;
361198893Srdivacky  }
362198893Srdivacky
363198893Srdivacky  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
364226633Sdim                                                    StringRef S) {
365234353Sdim
366218893Sdim    PD.AddString(S);
367218893Sdim    return PD;
368218893Sdim  }
369234353Sdim
370218893Sdim  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
371198092Srdivacky                                                    const SourceRange &R) {
372210299Sed    PD.AddSourceRange(CharSourceRange::getTokenRange(R));
373210299Sed    return PD;
374210299Sed  }
375210299Sed
376210299Sed  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
377210299Sed                                                    const CharSourceRange &R) {
378198092Srdivacky    PD.AddSourceRange(R);
379198092Srdivacky    return PD;
380198092Srdivacky  }
381234353Sdim
382198092Srdivacky  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
383206084Srdivacky                                             const FixItHint &Hint) {
384206084Srdivacky    PD.AddFixItHint(Hint);
385202379Srdivacky    return PD;
386202379Srdivacky  }
387234353Sdim
388198092Srdivacky};
389198092Srdivacky
390198092Srdivackyinline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
391198092Srdivacky                                           const PartialDiagnostic &PD) {
392198092Srdivacky  PD.Emit(DB);
393198092Srdivacky  return DB;
394198092Srdivacky}
395234353Sdim
396218893Sdim/// \brief A partial diagnostic along with the source location where this
397218893Sdim/// diagnostic occurs.
398218893Sdimtypedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt;
399198092Srdivacky
400198092Srdivacky}  // end namespace clang
401206084Srdivacky#endif
402