//===- 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 #include #include #include #include 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 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 ::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(~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 &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; } // namespace clang #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H