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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
