Home | History | Annotate | Line # | Download | only in Basic
      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/LLVM.h"
     20 #include "clang/Basic/SourceLocation.h"
     21 #include "llvm/ADT/SmallVector.h"
     22 #include "llvm/ADT/StringRef.h"
     23 #include <cassert>
     24 #include <cstdint>
     25 #include <string>
     26 #include <type_traits>
     27 #include <utility>
     28 
     29 namespace clang {
     30 
     31 class DeclContext;
     32 class IdentifierInfo;
     33 
     34 class PartialDiagnostic : public StreamingDiagnostic {
     35 private:
     36   // NOTE: Sema assumes that PartialDiagnostic is location-invariant
     37   // in the sense that its bits can be safely memcpy'ed and destructed
     38   // in the new location.
     39 
     40   /// The diagnostic ID.
     41   mutable unsigned DiagID = 0;
     42 public:
     43   struct NullDiagnostic {};
     44 
     45   /// Create a null partial diagnostic, which cannot carry a payload,
     46   /// and only exists to be swapped with a real partial diagnostic.
     47   PartialDiagnostic(NullDiagnostic) {}
     48 
     49   PartialDiagnostic(unsigned DiagID, DiagStorageAllocator &Allocator_)
     50       : StreamingDiagnostic(Allocator_), DiagID(DiagID) {}
     51 
     52   PartialDiagnostic(const PartialDiagnostic &Other)
     53       : StreamingDiagnostic(), DiagID(Other.DiagID) {
     54     Allocator = Other.Allocator;
     55     if (Other.DiagStorage) {
     56       DiagStorage = getStorage();
     57       *DiagStorage = *Other.DiagStorage;
     58     }
     59   }
     60 
     61   template <typename T> const PartialDiagnostic &operator<<(const T &V) const {
     62     const StreamingDiagnostic &DB = *this;
     63     DB << V;
     64     return *this;
     65   }
     66 
     67   // It is necessary to limit this to rvalue reference to avoid calling this
     68   // function with a bitfield lvalue argument since non-const reference to
     69   // bitfield is not allowed.
     70   template <typename T, typename = typename std::enable_if<
     71                             !std::is_lvalue_reference<T>::value>::type>
     72   const PartialDiagnostic &operator<<(T &&V) const {
     73     const StreamingDiagnostic &DB = *this;
     74     DB << std::move(V);
     75     return *this;
     76   }
     77 
     78   PartialDiagnostic(PartialDiagnostic &&Other) : DiagID(Other.DiagID) {
     79     Allocator = Other.Allocator;
     80     DiagStorage = Other.DiagStorage;
     81     Other.DiagStorage = nullptr;
     82   }
     83 
     84   PartialDiagnostic(const PartialDiagnostic &Other,
     85                     DiagnosticStorage *DiagStorage_)
     86       : DiagID(Other.DiagID) {
     87     Allocator = reinterpret_cast<DiagStorageAllocator *>(~uintptr_t(0));
     88     DiagStorage = DiagStorage_;
     89     if (Other.DiagStorage)
     90       *this->DiagStorage = *Other.DiagStorage;
     91   }
     92 
     93   PartialDiagnostic(const Diagnostic &Other, DiagStorageAllocator &Allocator_)
     94       : DiagID(Other.getID()) {
     95     Allocator = &Allocator_;
     96     // Copy arguments.
     97     for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
     98       if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
     99         AddString(Other.getArgStdStr(I));
    100       else
    101         AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
    102     }
    103 
    104     // Copy source ranges.
    105     for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
    106       AddSourceRange(Other.getRange(I));
    107 
    108     // Copy fix-its.
    109     for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
    110       AddFixItHint(Other.getFixItHint(I));
    111   }
    112 
    113   PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
    114     DiagID = Other.DiagID;
    115     if (Other.DiagStorage) {
    116       if (!DiagStorage)
    117         DiagStorage = getStorage();
    118 
    119       *DiagStorage = *Other.DiagStorage;
    120     } else {
    121       freeStorage();
    122     }
    123 
    124     return *this;
    125   }
    126 
    127   PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
    128     freeStorage();
    129 
    130     DiagID = Other.DiagID;
    131     DiagStorage = Other.DiagStorage;
    132     Allocator = Other.Allocator;
    133 
    134     Other.DiagStorage = nullptr;
    135     return *this;
    136   }
    137 
    138   void swap(PartialDiagnostic &PD) {
    139     std::swap(DiagID, PD.DiagID);
    140     std::swap(DiagStorage, PD.DiagStorage);
    141     std::swap(Allocator, PD.Allocator);
    142   }
    143 
    144   unsigned getDiagID() const { return DiagID; }
    145   void setDiagID(unsigned ID) { DiagID = ID; }
    146 
    147   void Emit(const DiagnosticBuilder &DB) const {
    148     if (!DiagStorage)
    149       return;
    150 
    151     // Add all arguments.
    152     for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
    153       if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
    154             == DiagnosticsEngine::ak_std_string)
    155         DB.AddString(DiagStorage->DiagArgumentsStr[i]);
    156       else
    157         DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
    158             (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
    159     }
    160 
    161     // Add all ranges.
    162     for (const CharSourceRange &Range : DiagStorage->DiagRanges)
    163       DB.AddSourceRange(Range);
    164 
    165     // Add all fix-its.
    166     for (const FixItHint &Fix : DiagStorage->FixItHints)
    167       DB.AddFixItHint(Fix);
    168   }
    169 
    170   void EmitToString(DiagnosticsEngine &Diags,
    171                     SmallVectorImpl<char> &Buf) const {
    172     // FIXME: It should be possible to render a diagnostic to a string without
    173     //        messing with the state of the diagnostics engine.
    174     DiagnosticBuilder DB(Diags.Report(getDiagID()));
    175     Emit(DB);
    176     Diagnostic(&Diags).FormatDiagnostic(Buf);
    177     DB.Clear();
    178     Diags.Clear();
    179   }
    180 
    181   /// Clear out this partial diagnostic, giving it a new diagnostic ID
    182   /// and removing all of its arguments, ranges, and fix-it hints.
    183   void Reset(unsigned DiagID = 0) {
    184     this->DiagID = DiagID;
    185     freeStorage();
    186   }
    187 
    188   bool hasStorage() const { return DiagStorage != nullptr; }
    189 
    190   /// Retrieve the string argument at the given index.
    191   StringRef getStringArg(unsigned I) {
    192     assert(DiagStorage && "No diagnostic storage?");
    193     assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
    194     assert(DiagStorage->DiagArgumentsKind[I]
    195              == DiagnosticsEngine::ak_std_string && "Not a string arg");
    196     return DiagStorage->DiagArgumentsStr[I];
    197   }
    198 };
    199 
    200 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
    201                                            const PartialDiagnostic &PD) {
    202   PD.Emit(DB);
    203   return DB;
    204 }
    205 
    206 /// A partial diagnostic along with the source location where this
    207 /// diagnostic occurs.
    208 using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
    209 
    210 } // namespace clang
    211 
    212 #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
    213