//===- PartialDiagnostic.h - Diagnostic "closures" --------------*- C++ -*-===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//
 
//===----------------------------------------------------------------------===//
 
//
 
/// \file
 
/// Implements a partial diagnostic that can be emitted anwyhere
 
/// in a DiagnosticBuilder stream.
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
 
#define LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
 
 
 
#include "clang/Basic/Diagnostic.h"
 
#include "clang/Basic/LLVM.h"
 
#include "clang/Basic/SourceLocation.h"
 
#include "llvm/ADT/SmallVector.h"
 
#include "llvm/ADT/StringRef.h"
 
#include <cassert>
 
#include <cstdint>
 
#include <string>
 
#include <type_traits>
 
#include <utility>
 
 
 
namespace clang {
 
 
 
class PartialDiagnostic : public StreamingDiagnostic {
 
private:
 
  // NOTE: Sema assumes that PartialDiagnostic is location-invariant
 
  // in the sense that its bits can be safely memcpy'ed and destructed
 
  // in the new location.
 
 
 
  /// The diagnostic ID.
 
  mutable unsigned DiagID = 0;
 
public:
 
  struct NullDiagnostic {};
 
 
 
  /// Create a null partial diagnostic, which cannot carry a payload,
 
  /// and only exists to be swapped with a real partial diagnostic.
 
  PartialDiagnostic(NullDiagnostic) {}
 
 
 
  PartialDiagnostic(unsigned DiagID, DiagStorageAllocator &Allocator_)
 
      : StreamingDiagnostic(Allocator_), DiagID(DiagID) {}
 
 
 
  PartialDiagnostic(const PartialDiagnostic &Other)
 
      : StreamingDiagnostic(), DiagID(Other.DiagID) {
 
    Allocator = Other.Allocator;
 
    if (Other.DiagStorage) {
 
      DiagStorage = getStorage();
 
      *DiagStorage = *Other.DiagStorage;
 
    }
 
  }
 
 
 
  template <typename T> const PartialDiagnostic &operator<<(const T &V) const {
 
    const StreamingDiagnostic &DB = *this;
 
    DB << V;
 
    return *this;
 
  }
 
 
 
  // It is necessary to limit this to rvalue reference to avoid calling this
 
  // function with a bitfield lvalue argument since non-const reference to
 
  // bitfield is not allowed.
 
  template <typename T,
 
            typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>>
 
  const PartialDiagnostic &operator<<(T &&V) const {
 
    const StreamingDiagnostic &DB = *this;
 
    DB << std::move(V);
 
    return *this;
 
  }
 
 
 
  PartialDiagnostic(PartialDiagnostic &&Other) : DiagID(Other.DiagID) {
 
    Allocator = Other.Allocator;
 
    DiagStorage = Other.DiagStorage;
 
    Other.DiagStorage = nullptr;
 
  }
 
 
 
  PartialDiagnostic(const PartialDiagnostic &Other,
 
                    DiagnosticStorage *DiagStorage_)
 
      : DiagID(Other.DiagID) {
 
    Allocator = reinterpret_cast<DiagStorageAllocator *>(~uintptr_t(0));
 
    DiagStorage = DiagStorage_;
 
    if (Other.DiagStorage)
 
      *this->DiagStorage = *Other.DiagStorage;
 
  }
 
 
 
  PartialDiagnostic(const Diagnostic &Other, DiagStorageAllocator &Allocator_)
 
      : DiagID(Other.getID()) {
 
    Allocator = &Allocator_;
 
    // Copy arguments.
 
    for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
 
      if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
 
        AddString(Other.getArgStdStr(I));
 
      else
 
        AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
 
    }
 
 
 
    // Copy source ranges.
 
    for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
 
      AddSourceRange(Other.getRange(I));
 
 
 
    // Copy fix-its.
 
    for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
 
      AddFixItHint(Other.getFixItHint(I));
 
  }
 
 
 
  PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
 
    DiagID = Other.DiagID;
 
    if (Other.DiagStorage) {
 
      if (!DiagStorage)
 
        DiagStorage = getStorage();
 
 
 
      *DiagStorage = *Other.DiagStorage;
 
    } else {
 
      freeStorage();
 
    }
 
 
 
    return *this;
 
  }
 
 
 
  PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
 
    freeStorage();
 
 
 
    DiagID = Other.DiagID;
 
    DiagStorage = Other.DiagStorage;
 
    Allocator = Other.Allocator;
 
 
 
    Other.DiagStorage = nullptr;
 
    return *this;
 
  }
 
 
 
  void swap(PartialDiagnostic &PD) {
 
    std::swap(DiagID, PD.DiagID);
 
    std::swap(DiagStorage, PD.DiagStorage);
 
    std::swap(Allocator, PD.Allocator);
 
  }
 
 
 
  unsigned getDiagID() const { return DiagID; }
 
  void setDiagID(unsigned ID) { DiagID = ID; }
 
 
 
  void Emit(const DiagnosticBuilder &DB) const {
 
    if (!DiagStorage)
 
      return;
 
 
 
    // Add all arguments.
 
    for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
 
      if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
 
            == DiagnosticsEngine::ak_std_string)
 
        DB.AddString(DiagStorage->DiagArgumentsStr[i]);
 
      else
 
        DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
 
            (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
 
    }
 
 
 
    // Add all ranges.
 
    for (const CharSourceRange &Range : DiagStorage->DiagRanges)
 
      DB.AddSourceRange(Range);
 
 
 
    // Add all fix-its.
 
    for (const FixItHint &Fix : DiagStorage->FixItHints)
 
      DB.AddFixItHint(Fix);
 
  }
 
 
 
  void EmitToString(DiagnosticsEngine &Diags,
 
                    SmallVectorImpl<char> &Buf) const {
 
    // FIXME: It should be possible to render a diagnostic to a string without
 
    //        messing with the state of the diagnostics engine.
 
    DiagnosticBuilder DB(Diags.Report(getDiagID()));
 
    Emit(DB);
 
    Diagnostic(&Diags).FormatDiagnostic(Buf);
 
    DB.Clear();
 
    Diags.Clear();
 
  }
 
 
 
  /// Clear out this partial diagnostic, giving it a new diagnostic ID
 
  /// and removing all of its arguments, ranges, and fix-it hints.
 
  void Reset(unsigned DiagID = 0) {
 
    this->DiagID = DiagID;
 
    freeStorage();
 
  }
 
 
 
  bool hasStorage() const { return DiagStorage != nullptr; }
 
 
 
  /// Retrieve the string argument at the given index.
 
  StringRef getStringArg(unsigned I) {
 
    assert(DiagStorage && "No diagnostic storage?");
 
    assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
 
    assert(DiagStorage->DiagArgumentsKind[I]
 
             == DiagnosticsEngine::ak_std_string && "Not a string arg");
 
    return DiagStorage->DiagArgumentsStr[I];
 
  }
 
};
 
 
 
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
 
                                           const PartialDiagnostic &PD) {
 
  PD.Emit(DB);
 
  return DB;
 
}
 
 
 
/// A partial diagnostic along with the source location where this
 
/// diagnostic occurs.
 
using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
 
 
 
} // namespace clang
 
 
 
#endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H