[Lldb-commits] [lldb] [LLDB] Add formatters for MSVC STL std::optional (PR #149545)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Jul 18 09:16:47 PDT 2025
https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/149545
>From 927e9c7c9bdcd46154095b9cf5f7e714344f48b5 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Fri, 18 Jul 2025 18:05:48 +0200
Subject: [PATCH] [LLDB] Add formatters for MSVC STL std::optional
---
.../Language/CPlusPlus/CPlusPlusLanguage.cpp | 29 ++++++++++++-------
.../Language/CPlusPlus/GenericOptional.cpp | 26 +++++++++++++++--
.../Plugins/Language/CPlusPlus/MsvcStl.h | 6 ++++
.../TestDataFormatterGenericOptional.py | 18 +++++++-----
4 files changed, 60 insertions(+), 19 deletions(-)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index a8ebde0b55815..a49990e11f86d 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1545,20 +1545,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
"std::bitset synthetic child", "^std::(__debug::)?bitset<.+>(( )?&)?$",
stl_deref_flags, true);
- AddCXXSynthetic(
- cpp_category_sp,
- lldb_private::formatters::LibStdcppOptionalSyntheticFrontEndCreator,
- "std::optional synthetic child", "^std::optional<.+>(( )?&)?$",
- stl_deref_flags, true);
-
AddCXXSummary(cpp_category_sp,
lldb_private::formatters::StdlibCoroutineHandleSummaryProvider,
"libstdc++ std::coroutine_handle summary provider",
libstdcpp_std_coroutine_handle_regex, stl_summary_flags, true);
- AddCXXSummary(cpp_category_sp,
- lldb_private::formatters::GenericOptionalSummaryProvider,
- "libstd++ std::optional summary provider",
- "^std::optional<.+>(( )?&)?$", stl_summary_flags, true);
}
static lldb_private::SyntheticChildrenFrontEnd *
@@ -1648,6 +1638,17 @@ GenericForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *children,
*valobj_sp);
}
+static SyntheticChildrenFrontEnd *
+GenericOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+ lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+
+ if (IsMsvcStlOptional(*valobj_sp))
+ return MsvcStlOptionalSyntheticFrontEndCreator(children, valobj_sp);
+ return LibStdcppOptionalSyntheticFrontEndCreator(children, valobj_sp);
+}
+
/// Load formatters that are formatting types from more than one STL
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
if (!cpp_category_sp)
@@ -1664,6 +1665,8 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
SyntheticChildren::Flags stl_synth_flags;
stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
false);
+ SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
+ stl_deref_flags.SetFrontEndWantsDereference();
using StringElementType = StringPrinter::StringElementType;
@@ -1712,6 +1715,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
AddCXXSynthetic(cpp_category_sp, GenericForwardListSyntheticFrontEndCreator,
"std::forward_list synthetic children",
"^std::forward_list<.+>(( )?&)?$", stl_synth_flags, true);
+ AddCXXSynthetic(cpp_category_sp, GenericOptionalSyntheticFrontEndCreator,
+ "std::optional synthetic children",
+ "^std::optional<.+>(( )?&)?$", stl_deref_flags, true);
AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
"MSVC STL/libstdc++ std::shared_ptr summary provider",
@@ -1739,6 +1745,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
TypeSummaryImplSP(new ScriptSummaryFormat(
stl_summary_flags,
"lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider")));
+ AddCXXSummary(cpp_category_sp, GenericOptionalSummaryProvider,
+ "MSVC STL/libstd++ std::optional summary provider",
+ "^std::optional<.+>(( )?&)?$", stl_summary_flags, true);
}
static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp
index c041f39022d10..7fc6eb55d4e3e 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp
@@ -9,6 +9,7 @@
#include "Generic.h"
#include "LibCxx.h"
#include "LibStdcpp.h"
+#include "MsvcStl.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/Target/Target.h"
@@ -32,6 +33,7 @@ class GenericOptionalFrontend : public SyntheticChildrenFrontEnd {
enum class StdLib {
LibCxx,
LibStdcpp,
+ MsvcStl,
};
GenericOptionalFrontend(ValueObject &valobj, StdLib stdlib);
@@ -77,7 +79,8 @@ lldb::ChildCacheState GenericOptionalFrontend::Update() {
else if (m_stdlib == StdLib::LibStdcpp) {
if (ValueObjectSP payload = m_backend.GetChildMemberWithName("_M_payload"))
engaged_sp = payload->GetChildMemberWithName("_M_engaged");
- }
+ } else if (m_stdlib == StdLib::MsvcStl)
+ engaged_sp = m_backend.GetChildMemberWithName("_Has_value");
if (!engaged_sp)
return lldb::ChildCacheState::eRefetch;
@@ -114,7 +117,12 @@ ValueObjectSP GenericOptionalFrontend::GetChildAtIndex(uint32_t _idx) {
ValueObjectSP candidate = val_sp->GetChildMemberWithName("_M_value");
if (candidate)
val_sp = candidate;
- }
+ } else if (m_stdlib == StdLib::MsvcStl)
+ // Same issue as with LibCxx
+ val_sp = m_backend.GetChildMemberWithName("_Has_value")
+ ->GetParent()
+ ->GetChildAtIndex(0)
+ ->GetChildMemberWithName("_Value");
if (!val_sp)
return ValueObjectSP();
@@ -143,3 +151,17 @@ SyntheticChildrenFrontEnd *formatters::LibcxxOptionalSyntheticFrontEndCreator(
GenericOptionalFrontend::StdLib::LibCxx);
return nullptr;
}
+
+bool formatters::IsMsvcStlOptional(ValueObject &valobj) {
+ if (auto valobj_sp = valobj.GetNonSyntheticValue())
+ return valobj_sp->GetChildMemberWithName("_Has_value") != nullptr;
+ return false;
+}
+
+SyntheticChildrenFrontEnd *formatters::MsvcStlOptionalSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new GenericOptionalFrontend(
+ *valobj_sp, GenericOptionalFrontend::StdLib::MsvcStl);
+ return nullptr;
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index 0f3db4b50eeaf..c08eecfdecee7 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -65,6 +65,12 @@ SyntheticChildrenFrontEnd *
MsvcStlListSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp);
+// MSVC STL std::optional<>
+bool IsMsvcStlOptional(ValueObject &valobj);
+SyntheticChildrenFrontEnd *
+MsvcStlOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
} // namespace formatters
} // namespace lldb_private
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
index 99d79a9f125b1..7bb4f75de4e59 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
@@ -3,12 +3,9 @@
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
-USE_LIBSTDCPP = "USE_LIBSTDCPP"
-USE_LIBCPP = "USE_LIBCPP"
-
class GenericOptionalDataFormatterTestCase(TestBase):
- def do_test_with_run_command(self, stdlib_type):
+ def do_test_with_run_command(self):
"""Test that that file and class static variables display correctly."""
# This is the function to remove the custom formats in order to have a
@@ -21,7 +18,6 @@ def cleanup():
self.addTearDownHook(cleanup)
- self.build(dictionary={stdlib_type: "1"})
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
bkpt = self.target().FindBreakpointByID(
@@ -100,7 +96,8 @@ def cleanup():
## We are skipping gcc version less that 5.1 since this test requires -std=c++17
@skipIf(compiler="gcc", compiler_version=["<", "5.1"])
def test_with_run_command_libcpp(self):
- self.do_test_with_run_command(USE_LIBCPP)
+ self.build(dictionary={"USE_LIBCPP": 1})
+ self.do_test_with_run_command()
@add_test_categories(["libstdcxx"])
## Clang 7.0 is the oldest Clang that can reliably parse newer libc++ versions
@@ -109,4 +106,11 @@ def test_with_run_command_libcpp(self):
## We are skipping gcc version less that 5.1 since this test requires -std=c++17
@skipIf(compiler="gcc", compiler_version=["<", "5.1"])
def test_with_run_command_libstdcpp(self):
- self.do_test_with_run_command(USE_LIBSTDCPP)
+ self.build(dictionary={"USE_LIBSTDCPP": 1})
+ self.do_test_with_run_command()
+
+ @add_test_categories(["msvcstl"])
+ def test_with_run_command_msvcstl(self):
+ # No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
+ self.build()
+ self.do_test_with_run_command()
More information about the lldb-commits
mailing list