Author: Daedie-git Date: 2026-08-21T21:57:59+02:00 New Revision: 9a9b50168182a0c51b2e8e12735d424a019932c2
URL: https://github.com/llvm/llvm-project/commit/9a9b50168182a0c51b2e8e12735d424a019932c2 DIFF: https://github.com/llvm/llvm-project/commit/9a9b50168182a0c51b2e8e12735d424a019932c2.diff LOG: [lldb] Add MSVC STL formatter for std::expected (#217243) Pretty-print MSVC STL `std::expected<T, E>` and `expected<void, E>`. Engaged values show `Value`; unexpected alternatives show `Unexpected`. The anonymous union is walked the same way as `optional` so PDB debug info still finds `_Value` / `_Unexpected`. Tests: generic C++23 MSVC STL coverage for engaged, unexpected, void, and reference cases using PDB debug info. Part of #24834 Assisted-by: Grok 4.6 Assisted-by: codex-5.6-high --------- Co-authored-by: Bjorn Schobben <[email protected]> Added: lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/Makefile lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/TestDataFormatterStdExpected.py lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/main.cpp Modified: lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h Removed: ################################################################################ diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index c34f63637dbb4..1a4717fe5fe9c 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -43,6 +43,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN MsvcStl.cpp MsvcStlAtomic.cpp MsvcStlDeque.cpp + MsvcStlExpected.cpp MsvcStlSmartPointer.cpp MsvcStlSpan.cpp MsvcStlTree.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 1ee1de2bef2ff..ca5aeed182dda 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1736,6 +1736,23 @@ GenericBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *children, return LibStdcppBitsetSyntheticFrontEndCreator(children, valobj_sp); } +static SyntheticChildrenFrontEnd * +GenericExpectedSyntheticFrontEndCreator(CXXSyntheticChildren *children, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + if (IsMsvcStlExpected(*valobj_sp)) + return MsvcStlExpectedSyntheticFrontEndCreator(children, valobj_sp); + return nullptr; +} + +static bool GenericExpectedSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + if (IsMsvcStlExpected(valobj)) + return MsvcStlExpectedSummaryProvider(valobj, stream, options); + return false; +} + /// Load formatters that are formatting types from more than one STL static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) @@ -2000,6 +2017,13 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, "MSVC STL/libstdc++ std::bitset summary provider", "^std::bitset<.+>(( )?&)?$", stl_summary_flags, true); + + AddCXXSynthetic(cpp_category_sp, GenericExpectedSyntheticFrontEndCreator, + "MSVC STL std::expected synthetic children", + "^std::expected<.+>(( )?&)?$", stl_deref_flags, true); + AddCXXSummary(cpp_category_sp, GenericExpectedSummaryProvider, + "MSVC STL std::expected summary provider", + "^std::expected<.+>(( )?&)?$", stl_summary_flags, true); } static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h index 7e2657a1a37c1..b1f6fce2b1da4 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -151,6 +151,14 @@ SyntheticChildrenFrontEnd * MsvcStlBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp); +// MSVC STL std::expected<> +bool IsMsvcStlExpected(ValueObject &valobj); +bool MsvcStlExpectedSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); +SyntheticChildrenFrontEnd * +MsvcStlExpectedSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp new file mode 100644 index 0000000000000..e122fadb1ec76 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp @@ -0,0 +1,101 @@ +//===----------------------------------------------------------------------===// +// +// 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 "MsvcStl.h" + +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "llvm/Support/ErrorExtras.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class MsvcStlExpectedFrontend : public SyntheticChildrenFrontEnd { +public: + MsvcStlExpectedFrontend(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj) { + if (valobj.GetTargetSP()) + Update(); + } + + llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { + if (!m_active) + return llvm::createStringErrorV("type has no child named '{0}'", name); + if (m_has_value) { + if (name == "Value" || name == "$$dereference$$") + return 0; + } else if (name == "Unexpected") { + return 0; + } + return llvm::createStringErrorV("type has no child named '{0}'", name); + } + + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_active ? 1U : 0U; + } + + ValueObjectSP GetChildAtIndex(uint32_t idx) override { + if (!m_active || idx != 0) + return {}; + return m_active->Clone(m_has_value ? "Value" : "Unexpected"); + } + + lldb::ChildCacheState Update() override { + m_active = nullptr; + m_has_value = false; + ValueObjectSP ns = m_backend.GetNonSyntheticValue(); + if (!ns) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP has_sp = ns->GetChildMemberWithName("_Has_value"); + if (!has_sp) + return lldb::ChildCacheState::eRefetch; + + m_has_value = has_sp->GetValueAsUnsigned(0) != 0; + // expected<void, E> has no value child when engaged. + m_active = + ns->GetChildMemberWithName(m_has_value ? "_Value" : "_Unexpected") + .get(); + return lldb::ChildCacheState::eRefetch; + } + +private: + ValueObject *m_active = nullptr; + bool m_has_value = false; +}; +} // namespace + +bool formatters::IsMsvcStlExpected(ValueObject &valobj) { + if (auto valobj_sp = valobj.GetNonSyntheticValue()) + return valobj_sp->GetChildMemberWithName("_Has_value") != nullptr; + return false; +} + +bool formatters::MsvcStlExpectedSummaryProvider(ValueObject &valobj, + Stream &stream, + const TypeSummaryOptions &) { + ValueObjectSP ns = valobj.GetNonSyntheticValue(); + if (!ns) + return false; + ValueObjectSP has_sp = ns->GetChildMemberWithName("_Has_value"); + if (!has_sp) + return false; + // Keep the summary consistent with std::optional. + stream.Printf(" Has Value=%s ", + has_sp->GetValueAsUnsigned(0) ? "true" : "false"); + return true; +} + +SyntheticChildrenFrontEnd *formatters::MsvcStlExpectedSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new MsvcStlExpectedFrontend(*valobj_sp); + return nullptr; +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/Makefile new file mode 100644 index 0000000000000..96c46af546658 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/Makefile @@ -0,0 +1,4 @@ +CXX_SOURCES := main.cpp +CXXFLAGS_EXTRAS := -std=c++23 + +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/TestDataFormatterStdExpected.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/TestDataFormatterStdExpected.py new file mode 100644 index 0000000000000..c465f8998d6c4 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/TestDataFormatterStdExpected.py @@ -0,0 +1,46 @@ +"""Test the MSVC STL std::expected formatter.""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class StdExpectedDataFormatterTestCase(TestBase): + SHARED_BUILD_TESTCASE = False + TEST_WITH_PDB_DEBUG_INFO = True + + @add_test_categories(["msvcstl"]) + def test_msvcstl(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp") + ) + + self.expect_var_path( + "ok", + summary=" Has Value=true ", + children=[ValueCheck(name="Value", value="7")], + ) + self.expect_var_path( + "err", + summary=" Has Value=false ", + children=[ValueCheck(name="Unexpected", value="42")], + ) + self.expect_var_path("void_ok", summary=" Has Value=true ", children=[]) + self.expect_var_path( + "void_err", + summary=" Has Value=false ", + children=[ValueCheck(name="Unexpected", value="11")], + ) + + self.expect_var_path( + "ok_ref", + summary=" Has Value=true ", + children=[ValueCheck(name="Value", value="7")], + ) + self.expect_var_path( + "err_ref", + summary=" Has Value=false ", + children=[ValueCheck(name="Unexpected", value="42")], + ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/main.cpp new file mode 100644 index 0000000000000..5079781c1017b --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/main.cpp @@ -0,0 +1,12 @@ +#include <expected> + +int main() { + std::expected<int, int> ok = 7; + std::expected<int, int> err = std::unexpected(42); + std::expected<void, int> void_ok; + std::expected<void, int> void_err = std::unexpected(11); + std::expected<int, int> &ok_ref = ok; + std::expected<int, int> &err_ref = err; + + return ok.value(); // break here +} _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
