//===- 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 = typename std::enable_if<
!std::is_lvalue_reference<T>::value>::type>
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