The LLVM Coding Standards [1] specify that: > [T]o match error message styles commonly produced by other tools, > start the first sentence with a lowercase letter, and finish the last > sentence without a period, if it would end in one otherwise. Historically, that hasn't been something we've enforced in LLDB, but in the past year or so I've started to pay more attention to this in code reviews. This PR brings more error messages in compliance, further increasing consistency. I also adopted `createStringErrorV` where it improved the code as a drive-by for lines I was already touching. [1] https://llvm.org/docs/CodingStandards.html#error-and-warning-messages Assisted-by: Claude Code
575 lines
17 KiB
C++
575 lines
17 KiB
C++
//===-- LibStdcpp.cpp -----------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "LibStdcpp.h"
|
|
#include "LibCxx.h"
|
|
|
|
#include "Plugins/Language/CPlusPlus/CxxStringTypes.h"
|
|
#include "Plugins/Language/CPlusPlus/Generic.h"
|
|
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
|
|
|
#include "lldb/DataFormatters/FormattersHelpers.h"
|
|
#include "lldb/DataFormatters/StringPrinter.h"
|
|
#include "lldb/DataFormatters/VectorIterator.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Utility/DataBufferHeap.h"
|
|
#include "lldb/Utility/Endian.h"
|
|
#include "lldb/Utility/Status.h"
|
|
#include "lldb/Utility/Stream.h"
|
|
#include "lldb/ValueObject/ValueObject.h"
|
|
#include "lldb/ValueObject/ValueObjectConstResult.h"
|
|
#include "llvm/Support/ErrorExtras.h"
|
|
#include <optional>
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::formatters;
|
|
|
|
namespace {
|
|
|
|
using StringElementType = StringPrinter::StringElementType;
|
|
|
|
class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
|
/*
|
|
(std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char,
|
|
std::char_traits<char>, std::allocator<char> > > >) ibeg = {
|
|
(_Base_ptr) _M_node = 0x0000000100103910 {
|
|
(std::_Rb_tree_color) _M_color = _S_black
|
|
(std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0
|
|
(std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000
|
|
(std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000
|
|
}
|
|
}
|
|
*/
|
|
|
|
public:
|
|
explicit LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
|
|
|
|
llvm::Expected<uint32_t> CalculateNumChildren() override;
|
|
|
|
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
|
|
|
|
lldb::ChildCacheState Update() override;
|
|
|
|
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
|
|
|
|
private:
|
|
ExecutionContextRef m_exe_ctx_ref;
|
|
lldb::addr_t m_pair_address = 0;
|
|
CompilerType m_pair_type;
|
|
lldb::ValueObjectSP m_pair_sp;
|
|
};
|
|
|
|
class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
|
public:
|
|
explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
|
|
|
|
llvm::Expected<uint32_t> CalculateNumChildren() override;
|
|
|
|
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
|
|
|
|
lldb::ChildCacheState Update() override;
|
|
|
|
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
|
|
|
|
private:
|
|
// The lifetime of a ValueObject and all its derivative ValueObjects
|
|
// (children, clones, etc.) is managed by a ClusterManager. These
|
|
// objects are only destroyed when every shared pointer to any of them
|
|
// is destroyed, so we must not store a shared pointer to any ValueObject
|
|
// derived from our backend ValueObject (since we're in the same cluster).
|
|
ValueObject *m_ptr_obj = nullptr; // Underlying pointer (held, not owned)
|
|
};
|
|
|
|
} // end of anonymous namespace
|
|
|
|
LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd(
|
|
lldb::ValueObjectSP valobj_sp)
|
|
: SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type(),
|
|
m_pair_sp() {
|
|
if (valobj_sp)
|
|
Update();
|
|
}
|
|
|
|
lldb::ChildCacheState LibstdcppMapIteratorSyntheticFrontEnd::Update() {
|
|
ValueObjectSP valobj_sp = m_backend.GetSP();
|
|
if (!valobj_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
TargetSP target_sp(valobj_sp->GetTargetSP());
|
|
|
|
if (!target_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8);
|
|
|
|
if (!valobj_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
|
|
|
|
ValueObjectSP _M_node_sp(valobj_sp->GetChildMemberWithName("_M_node"));
|
|
if (!_M_node_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
m_pair_address = _M_node_sp->GetValueAsUnsigned(0);
|
|
if (m_pair_address == 0)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
m_pair_address += (is_64bit ? 32 : 16);
|
|
|
|
CompilerType my_type(valobj_sp->GetCompilerType());
|
|
if (my_type.GetNumTemplateArguments() >= 1) {
|
|
CompilerType pair_type = my_type.GetTypeTemplateArgument(0);
|
|
if (!pair_type)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
m_pair_type = pair_type;
|
|
} else
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
return lldb::ChildCacheState::eReuse;
|
|
}
|
|
|
|
llvm::Expected<uint32_t>
|
|
LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
|
|
return 2;
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
|
|
if (m_pair_address != 0 && m_pair_type) {
|
|
if (!m_pair_sp)
|
|
m_pair_sp = CreateValueObjectFromAddress("pair", m_pair_address,
|
|
m_exe_ctx_ref, m_pair_type);
|
|
if (m_pair_sp)
|
|
return m_pair_sp->GetChildAtIndex(idx);
|
|
}
|
|
return lldb::ValueObjectSP();
|
|
}
|
|
|
|
llvm::Expected<size_t>
|
|
LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
|
|
ConstString name) {
|
|
if (name == "first")
|
|
return 0;
|
|
if (name == "second")
|
|
return 1;
|
|
return llvm::createStringErrorV("type has no child named '{0}'", name);
|
|
}
|
|
|
|
SyntheticChildrenFrontEnd *
|
|
lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator(
|
|
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
|
|
return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp)
|
|
: nullptr);
|
|
}
|
|
|
|
/*
|
|
(lldb) fr var ibeg --ptr-depth 1
|
|
(__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >)
|
|
ibeg = {
|
|
_M_current = 0x00000001001037a0 {
|
|
*_M_current = 1
|
|
}
|
|
}
|
|
*/
|
|
|
|
SyntheticChildrenFrontEnd *
|
|
lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator(
|
|
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
|
|
return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
|
|
valobj_sp, {ConstString("_M_current")})
|
|
: nullptr);
|
|
}
|
|
|
|
lldb_private::formatters::VectorIteratorSyntheticFrontEnd::
|
|
VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp,
|
|
llvm::ArrayRef<ConstString> item_names)
|
|
: SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
|
|
m_item_names(item_names), m_item_sp() {
|
|
if (valobj_sp)
|
|
Update();
|
|
}
|
|
|
|
lldb::ChildCacheState VectorIteratorSyntheticFrontEnd::Update() {
|
|
m_item_sp.reset();
|
|
|
|
ValueObjectSP valobj_sp = m_backend.GetSP();
|
|
if (!valobj_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
ValueObjectSP item_ptr =
|
|
formatters::GetChildMemberWithName(*valobj_sp, m_item_names);
|
|
if (!item_ptr)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
if (item_ptr->GetValueAsUnsigned(0) == 0)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
Status err;
|
|
m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
|
|
m_item_sp = CreateValueObjectFromAddress(
|
|
"item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref,
|
|
item_ptr->GetCompilerType().GetPointeeType());
|
|
if (err.Fail())
|
|
m_item_sp.reset();
|
|
return lldb::ChildCacheState::eRefetch;
|
|
}
|
|
|
|
llvm::Expected<uint32_t>
|
|
VectorIteratorSyntheticFrontEnd::CalculateNumChildren() {
|
|
return 1;
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
VectorIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
|
|
if (idx == 0)
|
|
return m_item_sp;
|
|
return lldb::ValueObjectSP();
|
|
}
|
|
|
|
llvm::Expected<size_t>
|
|
VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
|
|
if (name == "item")
|
|
return 0;
|
|
return llvm::createStringErrorV("type has no child named '{0}'", name);
|
|
}
|
|
|
|
bool lldb_private::formatters::LibStdcppStringSummaryProvider(
|
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
|
ValueObjectSP ptr = valobj.GetChildAtNamePath({"_M_dataplus", "_M_p"});
|
|
if (!ptr || !ptr->GetError().Success())
|
|
stream << "Summary Unavailable";
|
|
else
|
|
stream << ptr->GetSummaryAsCString();
|
|
|
|
return true;
|
|
}
|
|
|
|
template <StringPrinter::StringElementType element_type>
|
|
static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
|
|
const TypeSummaryOptions &summary_options,
|
|
std::string prefix_token) {
|
|
auto data_sp = valobj.GetChildMemberWithName("_M_str");
|
|
auto size_sp = valobj.GetChildMemberWithName("_M_len");
|
|
if (!data_sp || !size_sp)
|
|
return false;
|
|
|
|
bool success = false;
|
|
uint64_t size = size_sp->GetValueAsUnsigned(0, &success);
|
|
if (!success) {
|
|
stream << "Summary Unavailable";
|
|
return true;
|
|
}
|
|
|
|
StreamString scratch_stream;
|
|
success = StringBufferSummaryProvider<element_type>(
|
|
scratch_stream, summary_options, data_sp, size, prefix_token);
|
|
|
|
if (success)
|
|
stream << scratch_stream.GetData();
|
|
else
|
|
stream << "Summary Unavailable";
|
|
return true;
|
|
}
|
|
|
|
bool lldb_private::formatters::LibStdcppWStringViewSummaryProvider(
|
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
|
auto wchar_t_size = GetWCharByteSize(valobj);
|
|
if (!wchar_t_size)
|
|
return false;
|
|
|
|
switch (*wchar_t_size) {
|
|
case 1:
|
|
return formatStringViewImpl<StringElementType::UTF8>(valobj, stream,
|
|
options, "L");
|
|
case 2:
|
|
return formatStringViewImpl<StringElementType::UTF16>(valobj, stream,
|
|
options, "L");
|
|
case 4:
|
|
return formatStringViewImpl<StringElementType::UTF32>(valobj, stream,
|
|
options, "L");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <StringElementType element_type>
|
|
static constexpr const char *getPrefixToken() {
|
|
switch (element_type) {
|
|
case StringElementType::ASCII:
|
|
return "";
|
|
case StringElementType::UTF8:
|
|
return "u8";
|
|
case StringElementType::UTF16:
|
|
return "u";
|
|
case StringElementType::UTF32:
|
|
return "U";
|
|
}
|
|
llvm_unreachable("invalid element type");
|
|
}
|
|
|
|
template <StringPrinter::StringElementType element_type>
|
|
bool lldb_private::formatters::LibStdcppStringViewSummaryProvider(
|
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
|
return formatStringViewImpl<element_type>(valobj, stream, options,
|
|
getPrefixToken<element_type>());
|
|
}
|
|
|
|
template bool lldb_private::formatters::LibStdcppStringViewSummaryProvider<
|
|
StringElementType::ASCII>(ValueObject &, Stream &,
|
|
const TypeSummaryOptions &);
|
|
template bool lldb_private::formatters::LibStdcppStringViewSummaryProvider<
|
|
StringElementType::UTF8>(ValueObject &, Stream &,
|
|
const TypeSummaryOptions &);
|
|
template bool lldb_private::formatters::LibStdcppStringViewSummaryProvider<
|
|
StringElementType::UTF16>(ValueObject &, Stream &,
|
|
const TypeSummaryOptions &);
|
|
template bool lldb_private::formatters::LibStdcppStringViewSummaryProvider<
|
|
StringElementType::UTF32>(ValueObject &, Stream &,
|
|
const TypeSummaryOptions &);
|
|
|
|
LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(
|
|
lldb::ValueObjectSP valobj_sp)
|
|
: SyntheticChildrenFrontEnd(*valobj_sp) {
|
|
if (valobj_sp)
|
|
Update();
|
|
}
|
|
|
|
llvm::Expected<uint32_t>
|
|
LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
|
|
return 1;
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
|
|
if (!m_ptr_obj)
|
|
return nullptr;
|
|
|
|
if (idx == 0)
|
|
return m_ptr_obj->GetSP();
|
|
|
|
if (idx == 1) {
|
|
ValueObjectSP valobj_sp = m_backend.GetSP();
|
|
if (!valobj_sp)
|
|
return nullptr;
|
|
|
|
Status status;
|
|
ValueObjectSP value_sp = m_ptr_obj->Dereference(status);
|
|
if (status.Success())
|
|
return value_sp;
|
|
}
|
|
return lldb::ValueObjectSP();
|
|
}
|
|
|
|
lldb::ChildCacheState LibStdcppSharedPtrSyntheticFrontEnd::Update() {
|
|
auto backend = m_backend.GetSP();
|
|
if (!backend)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
auto valobj_sp = backend->GetNonSyntheticValue();
|
|
if (!valobj_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("_M_ptr");
|
|
if (!ptr_obj_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
auto cast_ptr_sp = GetDesugaredSmartPointerValue(*ptr_obj_sp, *valobj_sp);
|
|
if (!cast_ptr_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
m_ptr_obj = cast_ptr_sp->Clone(ConstString("pointer")).get();
|
|
|
|
return lldb::ChildCacheState::eRefetch;
|
|
}
|
|
|
|
llvm::Expected<size_t>
|
|
LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
|
|
if (name == "pointer")
|
|
return 0;
|
|
|
|
if (name == "object" || name == "$$dereference$$")
|
|
return 1;
|
|
|
|
return llvm::createStringErrorV("type has no child named '{0}'", name);
|
|
}
|
|
|
|
SyntheticChildrenFrontEnd *
|
|
lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(
|
|
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
|
|
return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp)
|
|
: nullptr);
|
|
}
|
|
|
|
bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider(
|
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
|
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
|
|
if (!valobj_sp)
|
|
return false;
|
|
|
|
ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("_M_ptr"));
|
|
if (!ptr_sp)
|
|
return false;
|
|
|
|
DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
|
|
|
|
ValueObjectSP pi_sp = valobj_sp->GetChildAtNamePath({"_M_refcount", "_M_pi"});
|
|
if (!pi_sp)
|
|
return false;
|
|
|
|
bool success;
|
|
uint64_t pi_addr = pi_sp->GetValueAsUnsigned(0, &success);
|
|
// Empty control field. We're done.
|
|
if (!success || pi_addr == 0)
|
|
return true;
|
|
|
|
int64_t shared_count = 0;
|
|
if (auto count_sp = pi_sp->GetChildMemberWithName("_M_use_count")) {
|
|
bool success;
|
|
shared_count = count_sp->GetValueAsSigned(0, &success);
|
|
if (!success)
|
|
return false;
|
|
|
|
stream.Printf(" strong=%" PRId64, shared_count);
|
|
}
|
|
|
|
// _M_weak_count is the number of weak references + (_M_use_count != 0).
|
|
if (auto weak_count_sp = pi_sp->GetChildMemberWithName("_M_weak_count")) {
|
|
bool success;
|
|
int64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);
|
|
if (!success)
|
|
return false;
|
|
|
|
stream.Printf(" weak=%" PRId64, count - (shared_count != 0));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static uint64_t LibStdcppVariantNposValue(size_t index_byte_size) {
|
|
switch (index_byte_size) {
|
|
case 1:
|
|
return 0xff;
|
|
case 2:
|
|
return 0xffff;
|
|
default:
|
|
return 0xffff'ffff;
|
|
}
|
|
}
|
|
|
|
bool formatters::LibStdcppVariantSummaryProvider(
|
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
|
ValueObjectSP valobj_sp = valobj.GetNonSyntheticValue();
|
|
if (!valobj_sp)
|
|
return false;
|
|
|
|
ValueObjectSP index_obj = valobj_sp->GetChildMemberWithName("_M_index");
|
|
ValueObjectSP data_obj = valobj_sp->GetChildMemberWithName("_M_u");
|
|
if (!index_obj || !data_obj)
|
|
return false;
|
|
|
|
auto index_bytes = index_obj->GetByteSize();
|
|
if (!index_bytes)
|
|
return false;
|
|
auto npos_value = LibStdcppVariantNposValue(*index_bytes);
|
|
auto index = index_obj->GetValueAsUnsigned(0);
|
|
if (index == npos_value) {
|
|
stream.Printf(" No Value");
|
|
return true;
|
|
}
|
|
|
|
auto variant_type =
|
|
valobj_sp->GetCompilerType().GetCanonicalType().GetNonReferenceType();
|
|
if (!variant_type)
|
|
return false;
|
|
if (index >= variant_type.GetNumTemplateArguments(true)) {
|
|
stream.Printf(" <Invalid>");
|
|
return true;
|
|
}
|
|
|
|
auto active_type = variant_type.GetTypeTemplateArgument(index, true);
|
|
stream << " Active Type = " << active_type.GetDisplayTypeName() << " ";
|
|
return true;
|
|
}
|
|
|
|
static std::optional<int64_t>
|
|
LibStdcppExtractOrderingValue(ValueObject &valobj) {
|
|
lldb::ValueObjectSP value_sp = valobj.GetChildMemberWithName("_M_value");
|
|
if (!value_sp)
|
|
return std::nullopt;
|
|
bool success;
|
|
int64_t value = value_sp->GetValueAsSigned(0, &success);
|
|
if (!success)
|
|
return std::nullopt;
|
|
return value;
|
|
}
|
|
|
|
bool lldb_private::formatters::LibStdcppPartialOrderingSummaryProvider(
|
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
|
std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
|
|
if (!value)
|
|
return false;
|
|
switch (*value) {
|
|
case -1:
|
|
stream << "less";
|
|
break;
|
|
case 0:
|
|
stream << "equivalent";
|
|
break;
|
|
case 1:
|
|
stream << "greater";
|
|
break;
|
|
case -128:
|
|
case 2:
|
|
stream << "unordered";
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool lldb_private::formatters::LibStdcppWeakOrderingSummaryProvider(
|
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
|
std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
|
|
if (!value)
|
|
return false;
|
|
switch (*value) {
|
|
case -1:
|
|
stream << "less";
|
|
break;
|
|
case 0:
|
|
stream << "equivalent";
|
|
break;
|
|
case 1:
|
|
stream << "greater";
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool lldb_private::formatters::LibStdcppStrongOrderingSummaryProvider(
|
|
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
|
std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
|
|
if (!value)
|
|
return false;
|
|
switch (*value) {
|
|
case -1:
|
|
stream << "less";
|
|
break;
|
|
case 0:
|
|
stream << "equal";
|
|
break;
|
|
case 1:
|
|
stream << "greater";
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|