[Lldb-commits] [lldb] [lldb] Automatic indexing for synthetic children of collections (PR #174885)
Dave Lee via lldb-commits
lldb-commits at lists.llvm.org
Wed Mar 4 10:03:11 PST 2026
https://github.com/kastiglione updated https://github.com/llvm/llvm-project/pull/174885
>From fa0ff222350da4d7841abcad9e232b0bfffdd9d2 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 7 Jan 2026 15:04:59 -0800
Subject: [PATCH 1/5] [lldb] Automatic indexing for synthetic children of
collections
Synthetic providers for collection types use a child name format of "[N]".
This changes the Python-SWIG bridge to automatically convert child names in this
convention to the index embedded in the string. With this change, collections will only
need to implement `get_child_index` for non-collection children. Some examples are
"hidden" children (properties), or to implement `$$dereference$$` support.
The automatic conversion applies to N values that are less than the number of children
reported by the synthetic provider.
---
lldb/bindings/python/python-wrapper.swig | 17 +++++++++++++++--
.../TestFrameVarDILArraySubscript.py | 10 +++++++++-
.../ArraySubscript/myArraySynthProvider.py | 11 -----------
3 files changed, 24 insertions(+), 14 deletions(-)
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 0ba152166522b..41551dc095bed 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -315,6 +315,19 @@ PyObject *lldb_private::python::SWIGBridge::LLDBSwigPython_GetChildAtIndex(PyObj
return result.release();
}
+static uint32_t ParseIndex(PyObject *implementor, llvm::StringRef name) {
+ unsigned long long retval;
+ if (name.consume_front("[") && !name.consumeInteger(10, retval) && name == "]") {
+ // Lazily calculate the number of children (capped to one past the given index).
+ uint32_t max = retval + 1;
+ size_t num_children =
+ lldb_private::python::SWIGBridge::LLDBSwigPython_CalculateNumChildren(implementor, max);
+ if (retval < num_children)
+ return (uint32_t)retval;
+ }
+ return UINT32_MAX;
+}
+
uint32_t lldb_private::python::SWIGBridge::LLDBSwigPython_GetIndexOfChildWithName(
PyObject * implementor, const char *child_name) {
PyErr_Cleaner py_err_cleaner(true);
@@ -323,7 +336,7 @@ uint32_t lldb_private::python::SWIGBridge::LLDBSwigPython_GetIndexOfChildWithNam
auto pfunc = self.ResolveName<PythonCallable>("get_child_index");
if (!pfunc.IsAllocated())
- return UINT32_MAX;
+ return ParseIndex(implementor, child_name);
llvm::Expected<PythonObject> result = pfunc.Call(PythonString(child_name));
@@ -332,7 +345,7 @@ uint32_t lldb_private::python::SWIGBridge::LLDBSwigPython_GetIndexOfChildWithNam
if (PyErr_Occurred()) {
PyErr_Clear(); // FIXME print this? do something else
- return UINT32_MAX;
+ return ParseIndex(implementor, child_name);
}
if (retval >= 0)
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
index 33d2e3c4fc2b2..5c34fb32abdbf 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
@@ -89,7 +89,7 @@ def test_subscript(self):
def test_subscript_synthetic(self):
self.build()
- lldbutil.run_to_source_breakpoint(
+ _, process, _, _ = lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
@@ -108,3 +108,11 @@ def test_subscript_synthetic(self):
"frame var 'ma_ptr[0]'",
substrs=["(myArray) ma_ptr[0] = ([0] = 7, [1] = 8, [2] = 9, [3] = 10)"],
)
+
+ frame = process.selected_thread.selected_frame
+ my_array = frame.var("ma")
+ for i in range(my_array.num_children):
+ idx = my_array.GetIndexOfChildWithName(f"[{i}]")
+ self.assertEqual(idx, i)
+ idx = my_array.GetIndexOfChildWithName(f"[{my_array.num_children + 1}]")
+ self.assertEqual(idx, lldb.UINT32_MAX)
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py
index 167899bd3907c..0d9e518b6fb9b 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py
@@ -20,14 +20,3 @@ def get_child_at_index(self, index):
if index >= max_idx:
return None
return arr.GetChildAtIndex(index)
-
- def get_child_index(self, name):
- if name == "[0]":
- return 0
- if name == "[1]":
- return
- if name == "[2]":
- return 2
- if name == "[3]":
- return 3
- return -1
>From 61b52d8b8663bf4dc5ce356551ac6255f0a058df Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 11 Feb 2026 13:26:25 -0800
Subject: [PATCH 2/5] Move default subscript parsing into ValueObjectSynthetic
---
lldb/bindings/python/python-wrapper.swig | 17 +---
.../lldb/DataFormatters/TypeSynthetic.h | 5 +-
lldb/source/DataFormatters/VectorType.cpp | 13 ---
.../Language/CPlusPlus/GenericBitset.cpp | 9 --
.../Language/CPlusPlus/GenericList.cpp | 8 --
.../Language/CPlusPlus/GenericOptional.cpp | 8 +-
.../Plugins/Language/CPlusPlus/LibCxxMap.cpp | 12 ---
.../Language/CPlusPlus/LibCxxTuple.cpp | 9 --
.../Language/CPlusPlus/LibCxxUnorderedMap.cpp | 13 ---
.../Language/CPlusPlus/LibCxxVariant.cpp | 9 --
.../Language/CPlusPlus/LibStdcppTuple.cpp | 12 ---
.../Language/CPlusPlus/MsvcStlTree.cpp | 13 ---
.../Language/CPlusPlus/MsvcStlTuple.cpp | 9 --
.../Language/CPlusPlus/MsvcStlVariant.cpp | 9 --
lldb/source/Plugins/Language/ObjC/NSArray.cpp | 34 --------
.../Plugins/Language/ObjC/NSDictionary.cpp | 83 -------------------
.../Plugins/Language/ObjC/NSIndexPath.cpp | 13 ---
lldb/source/Plugins/Language/ObjC/NSSet.cpp | 51 ------------
.../ValueObject/ValueObjectSynthetic.cpp | 35 ++++++--
.../TestFrameVarDILArraySubscript.py | 10 +--
.../ArraySubscript/myArraySynthProvider.py | 11 +++
.../synthetic_subscript/Makefile | 2 +
.../TestSyntheticSubscript.py | 23 +++++
.../data-formatter/synthetic_subscript/main.c | 12 +++
.../synthetic_subscript/thing_formatter.py | 15 ++++
25 files changed, 102 insertions(+), 333 deletions(-)
create mode 100644 lldb/test/API/functionalities/data-formatter/synthetic_subscript/Makefile
create mode 100644 lldb/test/API/functionalities/data-formatter/synthetic_subscript/TestSyntheticSubscript.py
create mode 100644 lldb/test/API/functionalities/data-formatter/synthetic_subscript/main.c
create mode 100644 lldb/test/API/functionalities/data-formatter/synthetic_subscript/thing_formatter.py
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 41551dc095bed..0ba152166522b 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -315,19 +315,6 @@ PyObject *lldb_private::python::SWIGBridge::LLDBSwigPython_GetChildAtIndex(PyObj
return result.release();
}
-static uint32_t ParseIndex(PyObject *implementor, llvm::StringRef name) {
- unsigned long long retval;
- if (name.consume_front("[") && !name.consumeInteger(10, retval) && name == "]") {
- // Lazily calculate the number of children (capped to one past the given index).
- uint32_t max = retval + 1;
- size_t num_children =
- lldb_private::python::SWIGBridge::LLDBSwigPython_CalculateNumChildren(implementor, max);
- if (retval < num_children)
- return (uint32_t)retval;
- }
- return UINT32_MAX;
-}
-
uint32_t lldb_private::python::SWIGBridge::LLDBSwigPython_GetIndexOfChildWithName(
PyObject * implementor, const char *child_name) {
PyErr_Cleaner py_err_cleaner(true);
@@ -336,7 +323,7 @@ uint32_t lldb_private::python::SWIGBridge::LLDBSwigPython_GetIndexOfChildWithNam
auto pfunc = self.ResolveName<PythonCallable>("get_child_index");
if (!pfunc.IsAllocated())
- return ParseIndex(implementor, child_name);
+ return UINT32_MAX;
llvm::Expected<PythonObject> result = pfunc.Call(PythonString(child_name));
@@ -345,7 +332,7 @@ uint32_t lldb_private::python::SWIGBridge::LLDBSwigPython_GetIndexOfChildWithNam
if (PyErr_Occurred()) {
PyErr_Clear(); // FIXME print this? do something else
- return ParseIndex(implementor, child_name);
+ return UINT32_MAX;
}
if (retval >= 0)
diff --git a/lldb/include/lldb/DataFormatters/TypeSynthetic.h b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
index 5779c5de0703f..80d5b987d70de 100644
--- a/lldb/include/lldb/DataFormatters/TypeSynthetic.h
+++ b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
@@ -46,7 +46,10 @@ class SyntheticChildrenFrontEnd {
virtual lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) = 0;
- virtual llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) = 0;
+ virtual llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) {
+ return llvm::createStringError("Type has no child named '%s'",
+ name.AsCString());
+ }
/// This function is assumed to always succeed and if it fails, the front-end
/// should know to deal with it in the correct way (most probably, by refusing
diff --git a/lldb/source/DataFormatters/VectorType.cpp b/lldb/source/DataFormatters/VectorType.cpp
index c2355fbfdcb2f..624f9de312bb3 100644
--- a/lldb/source/DataFormatters/VectorType.cpp
+++ b/lldb/source/DataFormatters/VectorType.cpp
@@ -271,19 +271,6 @@ class VectorTypeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
return lldb::ChildCacheState::eRefetch;
}
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
- }
-
private:
lldb::Format m_parent_format = eFormatInvalid;
lldb::Format m_item_format = eFormatInvalid;
diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
index f2521ec750875..1d522f0b4a724 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
@@ -28,15 +28,6 @@ class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd {
GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib);
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
- }
-
lldb::ChildCacheState Update() override;
llvm::Expected<uint32_t> CalculateNumChildren() override {
return m_elements.size();
diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
index 8c5ac31aef3f3..7dcc9a19098b5 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
@@ -124,14 +124,6 @@ template <StlType Stl> class ListIterator {
template <StlType Stl>
class AbstractListFrontEnd : public SyntheticChildrenFrontEnd {
public:
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
- }
lldb::ChildCacheState Update() override;
protected:
diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp
index 7fc6eb55d4e3e..e4c261a5411f7 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp
@@ -41,12 +41,8 @@ class GenericOptionalFrontend : public SyntheticChildrenFrontEnd {
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
if (name == "$$dereference$$")
return 0;
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
+ return llvm::createStringError("Type has no child named '%s'",
+ name.AsCString());
}
llvm::Expected<uint32_t> CalculateNumChildren() override {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
index 85766966f1554..d00a293929e17 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -197,8 +197,6 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
llvm::Expected<uint32_t>
CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair);
@@ -390,16 +388,6 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
return lldb::ChildCacheState::eRefetch;
}
-llvm::Expected<size_t> lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
- GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
-}
-
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp
index ebc6d92aabe05..3e4093509b6bc 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp
@@ -20,15 +20,6 @@ class TupleFrontEnd: public SyntheticChildrenFrontEnd {
Update();
}
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
- }
-
lldb::ChildCacheState Update() override;
llvm::Expected<uint32_t> CalculateNumChildren() override {
return m_elements.size();
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
index 5588208a3ef84..25e33dbde1229 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
@@ -40,8 +40,6 @@ class LibcxxStdUnorderedMapSyntheticFrontEnd
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
CompilerType GetNodeType();
CompilerType GetElementType(CompilerType table_type);
@@ -285,17 +283,6 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() {
return lldb::ChildCacheState::eRefetch;
}
-llvm::Expected<size_t>
-lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
- GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
-}
-
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
index 30fec4e2dde0f..2855fc1e0512a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
@@ -202,15 +202,6 @@ class VariantFrontEnd : public SyntheticChildrenFrontEnd {
Update();
}
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
- }
-
lldb::ChildCacheState Update() override;
llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }
ValueObjectSP GetChildAtIndex(uint32_t idx) override;
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp
index cf72265bfbad3..d4fcd32c6b54b 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp
@@ -32,8 +32,6 @@ class LibStdcppTupleSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
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
@@ -96,16 +94,6 @@ LibStdcppTupleSyntheticFrontEnd::CalculateNumChildren() {
return m_members.size();
}
-llvm::Expected<size_t>
-LibStdcppTupleSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
-}
-
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp
index ddf6c27a3e003..80dc0a7bf055f 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp
@@ -187,8 +187,6 @@ class MsvcStlTreeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
/// Returns the ValueObject for the _Tree_node at index \ref idx.
///
@@ -335,17 +333,6 @@ lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::Update() {
return lldb::ChildCacheState::eRefetch;
}
-llvm::Expected<size_t>
-lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetIndexOfChildWithName(
- ConstString name) {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
-}
-
lldb::ChildCacheState MsvcStlTreeIterSyntheticFrontEnd::Update() {
m_inner_sp = nullptr;
auto node_sp = m_backend.GetChildMemberWithName("_Ptr");
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTuple.cpp
index fe20b4c141a65..fd133550e00b7 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTuple.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTuple.cpp
@@ -20,15 +20,6 @@ class TupleFrontEnd : public SyntheticChildrenFrontEnd {
Update();
}
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
- }
-
lldb::ChildCacheState Update() override;
llvm::Expected<uint32_t> CalculateNumChildren() override {
return m_elements.size();
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVariant.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVariant.cpp
index 3e7647be48bb0..53b99d7a48170 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVariant.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVariant.cpp
@@ -141,15 +141,6 @@ class VariantFrontEnd : public SyntheticChildrenFrontEnd {
Update();
}
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- return *optional_idx;
- }
-
lldb::ChildCacheState Update() override;
llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }
ValueObjectSP GetChildAtIndex(uint32_t idx) override;
diff --git a/lldb/source/Plugins/Language/ObjC/NSArray.cpp b/lldb/source/Plugins/Language/ObjC/NSArray.cpp
index 25376e064879d..b1dc9ff7e48bf 100644
--- a/lldb/source/Plugins/Language/ObjC/NSArray.cpp
+++ b/lldb/source/Plugins/Language/ObjC/NSArray.cpp
@@ -56,8 +56,6 @@ class NSArrayMSyntheticFrontEndBase : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override = 0;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
protected:
virtual lldb::addr_t GetDataAddress() = 0;
@@ -218,8 +216,6 @@ class GenericNSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
ExecutionContextRef m_exe_ctx_ref;
uint8_t m_ptr_size = 8;
@@ -526,20 +522,6 @@ lldb_private::formatters::GenericNSArrayMSyntheticFrontEnd<D32, D64>::Update() {
: lldb::ChildCacheState::eRefetch;
}
-llvm::Expected<size_t> lldb_private::formatters::NSArrayMSyntheticFrontEndBase::
- GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
template <typename D32, typename D64>
lldb_private::formatters::GenericNSArrayMSyntheticFrontEnd<D32, D64>::
GenericNSArrayMSyntheticFrontEnd::~GenericNSArrayMSyntheticFrontEnd() {
@@ -615,22 +597,6 @@ lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
m_data_64 = nullptr;
}
-template <typename D32, typename D64, bool Inline>
-llvm::Expected<size_t>
-lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<
- D32, D64, Inline>::GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
template <typename D32, typename D64, bool Inline>
llvm::Expected<uint32_t>
lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<
diff --git a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
index 24e84899e683c..ffe6852a67f9d 100644
--- a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
+++ b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
@@ -110,8 +110,6 @@ class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
struct DataDescriptor_32 {
uint32_t _used : 26;
@@ -149,8 +147,6 @@ class NSConstantDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
ExecutionContextRef m_exe_ctx_ref;
CompilerType m_pair_type;
@@ -179,8 +175,6 @@ class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
struct DictionaryItemDescriptor {
lldb::addr_t key_ptr;
@@ -229,8 +223,6 @@ class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
struct DictionaryItemDescriptor {
lldb::addr_t key_ptr;
@@ -260,8 +252,6 @@ namespace Foundation1100 {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
struct DataDescriptor_32 {
uint32_t _used : 26;
@@ -586,20 +576,6 @@ lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
m_data_64 = nullptr;
}
-llvm::Expected<size_t> lldb_private::formatters::
- NSDictionaryISyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
llvm::Expected<uint32_t> lldb_private::formatters::
NSDictionaryISyntheticFrontEnd::CalculateNumChildren() {
if (!m_data_32 && !m_data_64)
@@ -724,20 +700,6 @@ lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
: SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(),
m_pair_type() {}
-llvm::Expected<size_t> lldb_private::formatters::
- NSCFDictionarySyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
llvm::Expected<uint32_t> lldb_private::formatters::
NSCFDictionarySyntheticFrontEnd::CalculateNumChildren() {
if (!m_hashtable.IsValid())
@@ -860,21 +822,6 @@ lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp) {}
-llvm::Expected<size_t>
-lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
- GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
llvm::Expected<uint32_t> lldb_private::formatters::
NSConstantDictionarySyntheticFrontEnd::CalculateNumChildren() {
return m_size;
@@ -1064,22 +1011,6 @@ lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
m_data_64 = nullptr;
}
-template <typename D32, typename D64>
-llvm::Expected<size_t>
-lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
- D32, D64>::GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
template <typename D32, typename D64>
llvm::Expected<uint32_t>
lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
@@ -1228,20 +1159,6 @@ lldb_private::formatters::Foundation1100::
m_data_64 = nullptr;
}
-llvm::Expected<size_t> lldb_private::formatters::Foundation1100::
- NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
llvm::Expected<uint32_t> lldb_private::formatters::Foundation1100::
NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
if (!m_data_32 && !m_data_64)
diff --git a/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp b/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp
index b5360195e91d2..23f711931f956 100644
--- a/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp
+++ b/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp
@@ -126,19 +126,6 @@ class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; }
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
- }
-
lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; }
protected:
diff --git a/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/lldb/source/Plugins/Language/ObjC/NSSet.cpp
index 150b233507128..44af668759f96 100644
--- a/lldb/source/Plugins/Language/ObjC/NSSet.cpp
+++ b/lldb/source/Plugins/Language/ObjC/NSSet.cpp
@@ -52,8 +52,6 @@ class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
struct DataDescriptor_32 {
uint32_t _used : 26;
@@ -88,8 +86,6 @@ class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
struct SetItemDescriptor {
lldb::addr_t item_ptr;
@@ -119,8 +115,6 @@ class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
lldb::ChildCacheState Update() override;
- llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
-
private:
struct SetItemDescriptor {
@@ -386,21 +380,6 @@ lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() {
m_data_64 = nullptr;
}
-llvm::Expected<size_t>
-lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName(
- ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
llvm::Expected<uint32_t>
lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() {
if (!m_data_32 && !m_data_64)
@@ -522,21 +501,6 @@ lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd(
: SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(),
m_pair_type() {}
-llvm::Expected<size_t>
-lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName(
- ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
llvm::Expected<uint32_t>
lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() {
if (!m_hashtable.IsValid())
@@ -661,21 +625,6 @@ lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<D32, D64>::
m_data_64 = nullptr;
}
-template <typename D32, typename D64>
-llvm::Expected<size_t> lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<
- D32, D64>::GetIndexOfChildWithName(ConstString name) {
- auto optional_idx = ExtractIndexFromString(name.AsCString());
- if (!optional_idx) {
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- }
- uint32_t idx = *optional_idx;
- if (idx >= CalculateNumChildrenIgnoringErrors())
- return llvm::createStringError("Type has no child named '%s'",
- name.AsCString());
- return idx;
-}
-
template <typename D32, typename D64>
llvm::Expected<uint32_t>
lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<
diff --git a/lldb/source/ValueObject/ValueObjectSynthetic.cpp b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
index 44e53bd5fd82e..af342c66fabed 100644
--- a/lldb/source/ValueObject/ValueObjectSynthetic.cpp
+++ b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
@@ -9,6 +9,7 @@
#include "lldb/ValueObject/ValueObjectSynthetic.h"
#include "lldb/Core/Value.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/DataFormatters/TypeSynthetic.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Utility/ConstString.h"
@@ -18,6 +19,7 @@
#include "lldb/ValueObject/ValueObject.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
#include <optional>
namespace lldb_private {
@@ -329,6 +331,21 @@ ValueObjectSynthetic::GetChildMemberWithName(llvm::StringRef name,
return GetChildAtIndex(*index_or_err, can_create);
}
+static std::optional<uint32_t> ParseSubscriptIndex(ValueObjectSynthetic &valobj,
+ llvm::StringRef name) {
+ auto maybe_index = formatters::ExtractIndexFromString(name.data());
+ if (!maybe_index)
+ return std::nullopt;
+
+ auto idx = *maybe_index;
+ // Prevent unnecessary work by limiting max to one past the index.
+ uint32_t max = idx + 1;
+ auto num_children = valobj.GetNumChildrenIgnoringErrors(max);
+ if (idx >= num_children)
+ return std::nullopt;
+ return idx;
+}
+
llvm::Expected<size_t>
ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) {
UpdateValueIfNeeded();
@@ -344,12 +361,20 @@ ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) {
}
if (!found_index && m_synth_filter_up != nullptr) {
- auto index_or_err = m_synth_filter_up->GetIndexOfChildWithName(name);
- if (!index_or_err)
- return index_or_err.takeError();
+ size_t index = SIZE_MAX;
+ if (auto index_or_err = m_synth_filter_up->GetIndexOfChildWithName(name)) {
+ index = *index_or_err;
+ } else {
+ // Provide automatic support for subscript child names ("[N]").
+ auto maybe_subscript = ParseSubscriptIndex(*this, name);
+ if (!maybe_subscript)
+ return index_or_err.takeError();
+ index = *maybe_subscript;
+ llvm::consumeError(index_or_err.takeError());
+ }
std::lock_guard<std::mutex> guard(m_child_mutex);
- m_name_toindex[name.GetCString()] = *index_or_err;
- return *index_or_err;
+ m_name_toindex[name.GetCString()] = index;
+ return index;
} else if (!found_index && m_synth_filter_up == nullptr) {
return llvm::createStringError("Type has no child named '%s'",
name.AsCString());
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
index 5c34fb32abdbf..33d2e3c4fc2b2 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
@@ -89,7 +89,7 @@ def test_subscript(self):
def test_subscript_synthetic(self):
self.build()
- _, process, _, _ = lldbutil.run_to_source_breakpoint(
+ lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
@@ -108,11 +108,3 @@ def test_subscript_synthetic(self):
"frame var 'ma_ptr[0]'",
substrs=["(myArray) ma_ptr[0] = ([0] = 7, [1] = 8, [2] = 9, [3] = 10)"],
)
-
- frame = process.selected_thread.selected_frame
- my_array = frame.var("ma")
- for i in range(my_array.num_children):
- idx = my_array.GetIndexOfChildWithName(f"[{i}]")
- self.assertEqual(idx, i)
- idx = my_array.GetIndexOfChildWithName(f"[{my_array.num_children + 1}]")
- self.assertEqual(idx, lldb.UINT32_MAX)
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py
index 0d9e518b6fb9b..7549128d9b640 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py
@@ -20,3 +20,14 @@ def get_child_at_index(self, index):
if index >= max_idx:
return None
return arr.GetChildAtIndex(index)
+
+ def get_child_index(self, name):
+ if name == "[0]":
+ return 0
+ if name == "[1]":
+ return 1
+ if name == "[2]":
+ return 2
+ if name == "[3]":
+ return 3
+ return -1
diff --git a/lldb/test/API/functionalities/data-formatter/synthetic_subscript/Makefile b/lldb/test/API/functionalities/data-formatter/synthetic_subscript/Makefile
new file mode 100644
index 0000000000000..c9319d6e6888a
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/synthetic_subscript/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/synthetic_subscript/TestSyntheticSubscript.py b/lldb/test/API/functionalities/data-formatter/synthetic_subscript/TestSyntheticSubscript.py
new file mode 100644
index 0000000000000..be34b0f1f72d8
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/synthetic_subscript/TestSyntheticSubscript.py
@@ -0,0 +1,23 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ _, process, _, _ = lldbutil.run_to_source_breakpoint(
+ self, "break here", lldb.SBFileSpec("main.c")
+ )
+ self.runCmd("command script import thing_formatter.py")
+ frame = process.selected_thread.selected_frame
+ x = frame.var("x")
+ names = ("zero", "one")
+ for i in range(x.num_children):
+ idx = x.GetIndexOfChildWithName(f"[{i}]")
+ self.assertEqual(idx, i)
+ child = x.GetChildAtIndex(idx)
+ self.assertEqual(child.name, names[idx])
+ idx = x.GetIndexOfChildWithName(f"[{x.num_children + 1}]")
+ self.assertEqual(idx, lldb.UINT32_MAX)
diff --git a/lldb/test/API/functionalities/data-formatter/synthetic_subscript/main.c b/lldb/test/API/functionalities/data-formatter/synthetic_subscript/main.c
new file mode 100644
index 0000000000000..ca0da120a7c0c
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/synthetic_subscript/main.c
@@ -0,0 +1,12 @@
+struct Thing {
+ int zero;
+ int one;
+};
+
+int main() {
+ struct Thing x;
+ x.zero = 1;
+ x.one = 2;
+ __builtin_printf("break here\n");
+ return 0;
+}
diff --git a/lldb/test/API/functionalities/data-formatter/synthetic_subscript/thing_formatter.py b/lldb/test/API/functionalities/data-formatter/synthetic_subscript/thing_formatter.py
new file mode 100644
index 0000000000000..0027f0ba0be68
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/synthetic_subscript/thing_formatter.py
@@ -0,0 +1,15 @@
+class ThingSynthetic:
+ def __init__(self, valobj, _) -> None:
+ self.valobj = valobj
+
+ def num_children(self):
+ return self.valobj.num_children
+
+ def get_child_at_index(self, idx):
+ return self.valobj.GetChildAtIndex(idx)
+
+ # Use default implementation of get_child_index.
+
+
+def __lldb_init_module(dbg, _):
+ dbg.HandleCommand(f"type synthetic add -l {__name__}.ThingSynthetic Thing")
>From 5931bdee706a49b3f67e166399ae30f313279246 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Thu, 12 Feb 2026 09:46:23 -0800
Subject: [PATCH 3/5] Rename a local var
---
lldb/source/ValueObject/ValueObjectSynthetic.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lldb/source/ValueObject/ValueObjectSynthetic.cpp b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
index af342c66fabed..9e0026c11c233 100644
--- a/lldb/source/ValueObject/ValueObjectSynthetic.cpp
+++ b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
@@ -366,10 +366,10 @@ ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) {
index = *index_or_err;
} else {
// Provide automatic support for subscript child names ("[N]").
- auto maybe_subscript = ParseSubscriptIndex(*this, name);
- if (!maybe_subscript)
+ auto maybe_index = ParseSubscriptIndex(*this, name);
+ if (!maybe_index)
return index_or_err.takeError();
- index = *maybe_subscript;
+ index = *maybe_index;
llvm::consumeError(index_or_err.takeError());
}
std::lock_guard<std::mutex> guard(m_child_mutex);
>From 423902d797f336654ea91bb78c8c342e0fb7844e Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Tue, 3 Mar 2026 16:46:51 -0800
Subject: [PATCH 4/5] Add CustomSubscripting TypeOption
---
lldb/include/lldb/DataFormatters/TypeSynthetic.h | 14 ++++++++++++++
lldb/include/lldb/lldb-enumerations.h | 3 ++-
lldb/source/ValueObject/ValueObjectSynthetic.cpp | 2 +-
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/lldb/include/lldb/DataFormatters/TypeSynthetic.h b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
index 80d5b987d70de..788949e1cc273 100644
--- a/lldb/include/lldb/DataFormatters/TypeSynthetic.h
+++ b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
@@ -225,6 +225,18 @@ class SyntheticChildren {
return *this;
}
+ bool GetCustomSubscripting() const {
+ return m_flags & lldb::eTypeOptionCustomSubscripting;
+ }
+
+ Flags &SetCustomSubscripting(bool value = true) {
+ if (value)
+ m_flags |= lldb::eTypeOptionCustomSubscripting;
+ else
+ m_flags &= ~lldb::eTypeOptionCustomSubscripting;
+ return *this;
+ }
+
uint32_t GetValue() { return m_flags; }
void SetValue(uint32_t value) { m_flags = value; }
@@ -247,6 +259,8 @@ class SyntheticChildren {
bool WantsDereference() const { return m_flags.GetFrontEndWantsDereference();}
+ bool CustomSubscripting() const { return m_flags.GetCustomSubscripting(); }
+
void SetCascades(bool value) { m_flags.SetCascades(value); }
void SetSkipsPointers(bool value) { m_flags.SetSkipPointers(value); }
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 4cbbabbf879ad..f9c26d01e939a 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -926,7 +926,8 @@ FLAGS_ENUM(TypeOptions){eTypeOptionNone = (0u),
eTypeOptionHideNames = (1u << 6),
eTypeOptionNonCacheable = (1u << 7),
eTypeOptionHideEmptyAggregates = (1u << 8),
- eTypeOptionFrontEndWantsDereference = (1u << 9)};
+ eTypeOptionFrontEndWantsDereference = (1u << 9),
+ eTypeOptionCustomSubscripting = (1u << 10)};
/// This is the return value for frame comparisons. If you are comparing frame
/// A to frame B the following cases arise:
diff --git a/lldb/source/ValueObject/ValueObjectSynthetic.cpp b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
index 9e0026c11c233..215b249c343b5 100644
--- a/lldb/source/ValueObject/ValueObjectSynthetic.cpp
+++ b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
@@ -364,7 +364,7 @@ ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) {
size_t index = SIZE_MAX;
if (auto index_or_err = m_synth_filter_up->GetIndexOfChildWithName(name)) {
index = *index_or_err;
- } else {
+ } else if (!m_synth_sp->CustomSubscripting()) {
// Provide automatic support for subscript child names ("[N]").
auto maybe_index = ParseSubscriptIndex(*this, name);
if (!maybe_index)
>From 414043cdbb9bcc1c57da82a009829136cc82b02c Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 4 Mar 2026 10:00:47 -0800
Subject: [PATCH 5/5] Inline inadequately named ParseSubscriptIndex
---
.../ValueObject/ValueObjectSynthetic.cpp | 27 ++++++++-----------
1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/lldb/source/ValueObject/ValueObjectSynthetic.cpp b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
index 215b249c343b5..ed4b275cba758 100644
--- a/lldb/source/ValueObject/ValueObjectSynthetic.cpp
+++ b/lldb/source/ValueObject/ValueObjectSynthetic.cpp
@@ -331,21 +331,6 @@ ValueObjectSynthetic::GetChildMemberWithName(llvm::StringRef name,
return GetChildAtIndex(*index_or_err, can_create);
}
-static std::optional<uint32_t> ParseSubscriptIndex(ValueObjectSynthetic &valobj,
- llvm::StringRef name) {
- auto maybe_index = formatters::ExtractIndexFromString(name.data());
- if (!maybe_index)
- return std::nullopt;
-
- auto idx = *maybe_index;
- // Prevent unnecessary work by limiting max to one past the index.
- uint32_t max = idx + 1;
- auto num_children = valobj.GetNumChildrenIgnoringErrors(max);
- if (idx >= num_children)
- return std::nullopt;
- return idx;
-}
-
llvm::Expected<size_t>
ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) {
UpdateValueIfNeeded();
@@ -366,10 +351,20 @@ ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) {
index = *index_or_err;
} else if (!m_synth_sp->CustomSubscripting()) {
// Provide automatic support for subscript child names ("[N]").
- auto maybe_index = ParseSubscriptIndex(*this, name);
+ auto maybe_index = formatters::ExtractIndexFromString(name.GetCString());
if (!maybe_index)
+ // The child name was not of the form "[N]", return the original error.
return index_or_err.takeError();
+
index = *maybe_index;
+ // Prevent unnecessary work by limiting max to one past the index.
+ uint32_t max = index + 1;
+ auto num_children = GetNumChildrenIgnoringErrors(max);
+ if (index >= num_children)
+ return llvm::createStringError("Subscript index out of range: %zu",
+ index);
+
+ // Subscripting succeeded, ignore the original error.
llvm::consumeError(index_or_err.takeError());
}
std::lock_guard<std::mutex> guard(m_child_mutex);
More information about the lldb-commits
mailing list