207 lines
6.3 KiB
C++
207 lines
6.3 KiB
C++
//===- 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 {
|
|
DiagnosticBuilder DB(Diags.Report(getDiagID()));
|
|
Emit(DB);
|
|
Diagnostic(&Diags, DB).FormatDiagnostic(Buf);
|
|
DB.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
|