[Lldb-commits] [lldb] [lldb] Resolve ObjC property backing storage in dwim-print (PR #225204)

via lldb-commits lldb-commits at lists.llvm.org
Wed Sep 23 09:47:23 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Piotr Jeremicz (piotrekjeremicz)

<details>
<summary>Changes</summary>

Follow-up to #<!-- -->215776 (DWARF/LLVM IR representation) and #<!-- -->220362 (Clang emission) - this teaches LLDB to actually consume DW_TAG_property / DW_AT_property_forward.

A synthesized Objective-C property whose backing ivar has a different name than the property itself (e.g. `@<!-- -->synthesize declaredBacking = _customDeclaredIvar;`) can't be resolved by a plain `frame variable` lookup - the property name never names a real member, only the ivar does. `dwim-print` ("p") therefore always fell back to a full JIT-compiled `expression`, even though nothing about reading the property actually requires evaluation.

Assisted-by: Claude

---

Patch is 21.51 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/225204.diff


11 Files Affected:

- (modified) lldb/include/lldb/Symbol/CompilerType.h (+3) 
- (modified) lldb/include/lldb/Symbol/TypeSystem.h (+6) 
- (modified) lldb/source/Commands/CommandObjectDWIMPrint.cpp (+76-9) 
- (modified) lldb/source/Commands/CommandObjectDWIMPrint.h (+6) 
- (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp (+67-5) 
- (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h (+9) 
- (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp (+42-13) 
- (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h (+4) 
- (modified) lldb/source/Symbol/CompilerType.cpp (+10) 
- (modified) lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py (+21) 
- (modified) lldb/test/API/commands/dwim-print/objc/main.m (+19) 


``````````diff
diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index 168946cf17475e..da22b35a619bf2 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -485,6 +485,9 @@ class CompilerType {
 
   CompilerType GetDirectNestedTypeWithName(llvm::StringRef name) const;
 
+  llvm::StringRef
+  GetPropertyBackingStorageName(llvm::StringRef property_name) const;
+
   /// Return the number of template arguments the type has.
   /// If expand_pack is true, then variadic argument packs are automatically
   /// expanded to their supplied arguments. If it is false an argument pack
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 9ec0f13814975a..400bb0726a6274 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -403,6 +403,12 @@ class TypeSystem : public PluginInterface,
     return CompilerType();
   }
 
+  virtual llvm::StringRef
+  GetPropertyBackingStorageName(lldb::opaque_compiler_type_t type,
+                                llvm::StringRef property_name) {
+    return llvm::StringRef();
+  }
+
   virtual bool IsTemplateType(lldb::opaque_compiler_type_t type);
 
   virtual size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type,
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 1b0b4c7881cfc1..7b4be1692ae27e 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -22,6 +22,7 @@
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
 #include "lldb/lldb-forward.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 
 #include <regex>
@@ -51,6 +52,52 @@ CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter &interpreter)
 
 Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; }
 
+std::string CommandObjectDWIMPrint::RewritePathForBackingStorage(
+    llvm::StringRef expr, StackFrame &frame,
+    lldb::DynamicValueType use_dynamic) {
+  llvm::SmallVector<llvm::StringRef, 4> components;
+  expr.split(components, '.');
+  if (components.empty())
+    return {};
+
+  VariableSP var_sp;
+  Status status;
+  ValueObjectSP valobj_sp = frame.GetValueForVariableExpressionPath(
+      components[0], use_dynamic,
+      StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
+          StackFrame::eExpressionPathOptionsDisallowGlobals,
+      var_sp, status, lldb::eDILModeSimple);
+  if (!valobj_sp || !status.Success() || valobj_sp->GetError().Fail())
+    return {};
+
+  std::string rewritten_path = components[0].str();
+  bool did_rewrite = false;
+
+  for (llvm::StringRef component : llvm::ArrayRef(components).drop_front()) {
+    ValueObjectSP child_sp = valobj_sp->GetChildMemberWithName(component);
+    llvm::StringRef name_used = component;
+    if (!child_sp) {
+      llvm::StringRef backing_name =
+          valobj_sp->GetCompilerType().GetPropertyBackingStorageName(component);
+      if (backing_name.empty())
+        return {};
+      child_sp = valobj_sp->GetChildMemberWithName(backing_name);
+      if (!child_sp)
+        return {};
+      name_used = backing_name;
+      did_rewrite = true;
+    }
+    rewritten_path += '.';
+    rewritten_path += name_used;
+    valobj_sp = child_sp;
+  }
+
+  if (!did_rewrite)
+    return {};
+
+  return rewritten_path;
+}
+
 void CommandObjectDWIMPrint::DoExecute(StringRef command,
                                        CommandReturnObject &result) {
   m_option_group.NotifyOptionParsingStarting(&m_exe_ctx);
@@ -165,14 +212,34 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
   const bool try_variable_path =
       expr.find_first_of("*&->[]") == StringRef::npos;
   if (frame && try_variable_path) {
-    VariableSP var_sp;
-    Status status;
-    auto valobj_sp = frame->GetValueForVariableExpressionPath(
-        expr, eval_options.GetUseDynamic(),
-        StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
-            StackFrame::eExpressionPathOptionsDisallowGlobals,
-        var_sp, status, lldb::eDILModeSimple);
-    if (valobj_sp && status.Success() && valobj_sp->GetError().Success()) {
+    auto try_variable_expr = [&](llvm::StringRef path) -> ValueObjectSP {
+      VariableSP var_sp;
+      Status status;
+      auto valobj_sp = frame->GetValueForVariableExpressionPath(
+          path, eval_options.GetUseDynamic(),
+          StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
+              StackFrame::eExpressionPathOptionsDisallowGlobals,
+          var_sp, status, lldb::eDILModeSimple);
+      if (valobj_sp && status.Success() && valobj_sp->GetError().Success())
+        return valobj_sp;
+      return nullptr;
+    };
+
+    StringRef used_path = expr;
+    ValueObjectSP valobj_sp = try_variable_expr(expr);
+
+    std::string rewritten_path;
+    if (!valobj_sp) {
+      rewritten_path = RewritePathForBackingStorage(
+          expr, *frame, eval_options.GetUseDynamic());
+      if (!rewritten_path.empty()) {
+        valobj_sp = try_variable_expr(rewritten_path);
+        if (valobj_sp)
+          used_path = rewritten_path;
+      }
+    }
+
+    if (valobj_sp) {
       if (!suppress_result) {
         if (auto persisted_valobj = valobj_sp->Persist())
           valobj_sp = persisted_valobj;
@@ -183,7 +250,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
         if (args.HasArgs())
           flags = args.GetArgString();
         result.AppendNoteWithFormatv("ran `frame variable {0}{1}`", flags,
-                                     expr);
+                                     used_path);
       }
 
       dump_val_object(*valobj_sp);
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.h b/lldb/source/Commands/CommandObjectDWIMPrint.h
index 01ba9c225e3301..dd7b9ea7cb77a2 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.h
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.h
@@ -17,6 +17,8 @@
 
 namespace lldb_private {
 
+class StackFrame;
+
 /// Implements `dwim-print`, a printing command that chooses the most direct,
 /// efficient, and resilient means of printing a given expression.
 ///
@@ -42,6 +44,10 @@ class CommandObjectDWIMPrint : public CommandObjectRaw {
 private:
   void DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
 
+  std::string RewritePathForBackingStorage(llvm::StringRef expr,
+                                           StackFrame &frame,
+                                           lldb::DynamicValueType use_dynamic);
+
   OptionGroupOptions m_option_group;
   OptionGroupFormat m_format_options = lldb::eFormatDefault;
   OptionGroupValueObjectDisplay m_varobj_options;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index b60f1d9e41958a..c039aa3234a07f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -48,6 +48,7 @@
 #include "clang/AST/Type.h"
 #include "clang/Basic/Specifiers.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
 #include "llvm/DebugInfo/DWARF/DWARFTypePrinter.h"
 #include "llvm/Demangle/Demangle.h"
@@ -1983,21 +1984,41 @@ class DWARFASTParserClang::DelayedAddObjCClassProperty {
                                                 // required if you don't have an
                                                 // ivar decl
       const char *property_setter_name, const char *property_getter_name,
-      uint32_t property_attributes, ClangASTMetadata metadata)
+      uint32_t property_attributes, ClangASTMetadata metadata,
+      llvm::StringRef property_ivar_name)
       : m_class_opaque_type(class_opaque_type), m_property_name(property_name),
         m_property_opaque_type(property_opaque_type),
         m_property_setter_name(property_setter_name),
         m_property_getter_name(property_getter_name),
-        m_property_attributes(property_attributes), m_metadata(metadata) {}
+        m_property_attributes(property_attributes), m_metadata(metadata),
+        m_property_ivar_name(property_ivar_name) {}
 
   bool Finalize() {
     return TypeSystemClang::AddObjCClassProperty(
         m_class_opaque_type, m_property_name, m_property_opaque_type,
-        /*ivar_decl=*/nullptr, m_property_setter_name, m_property_getter_name,
+        FindIvarDecl(), m_property_setter_name, m_property_getter_name,
         m_property_attributes, m_metadata);
   }
 
 private:
+  clang::ObjCIvarDecl *FindIvarDecl() {
+    if (m_property_ivar_name.empty())
+      return nullptr;
+
+    clang::ObjCInterfaceDecl *class_interface_decl =
+        TypeSystemClang::GetAsObjCInterfaceDecl(m_class_opaque_type);
+    if (!class_interface_decl)
+      return nullptr;
+
+    auto ast = m_class_opaque_type.GetTypeSystem<TypeSystemClang>();
+    if (!ast)
+      return nullptr;
+
+    clang::IdentifierInfo &ivar_ident =
+        ast->getASTContext().Idents.get(m_property_ivar_name);
+    return class_interface_decl->lookupInstanceVariable(&ivar_ident);
+  }
+
   CompilerType m_class_opaque_type;
   const char *m_property_name;
   CompilerType m_property_opaque_type;
@@ -2005,6 +2026,7 @@ class DWARFASTParserClang::DelayedAddObjCClassProperty {
   const char *m_property_getter_name;
   uint32_t m_property_attributes;
   ClangASTMetadata m_metadata;
+  llvm::StringRef m_property_ivar_name;
 };
 
 static std::optional<clang::APValue> MakeAPValue(const clang::ASTContext &ast,
@@ -2831,9 +2853,39 @@ PropertyAttributes::PropertyAttributes(const DWARFDIE &die) {
   }
 }
 
+DWARFASTParserClang::PropertyBackingStorageNames
+DWARFASTParserClang::ParsePropertyBackingStorageNames(
+    const DWARFDIE &parent_die) {
+  PropertyBackingStorageNames property_backing_names;
+
+  for (DWARFDIE die : parent_die.children()) {
+    if (die.Tag() != DW_TAG_property)
+      continue;
+
+    const char *prop_name = die.GetName();
+    if (!prop_name)
+      continue;
+
+    for (DWARFDIE child_die : die.children()) {
+      if (child_die.Tag() != DW_TAG_property_getter)
+        continue;
+
+      if (DWARFDIE backing_die = child_die.GetAttributeValueAsReferenceDIE(
+              DW_AT_property_forward)) {
+        if (const char *backing_name = backing_die.GetName())
+          property_backing_names[prop_name] = backing_name;
+      }
+      break;
+    }
+  }
+
+  return property_backing_names;
+}
+
 void DWARFASTParserClang::ParseObjCProperty(
     const DWARFDIE &die, const DWARFDIE &parent_die,
     const lldb_private::CompilerType &class_clang_type,
+    const PropertyBackingStorageNames &property_backing_names,
     DelayedPropertyList &delayed_properties) {
   // This function can only parse DW_TAG_APPLE_property.
   assert(die.Tag() == DW_TAG_APPLE_property);
@@ -2859,12 +2911,18 @@ void DWARFASTParserClang::ParseObjCProperty(
     return;
   }
 
+  llvm::StringRef property_ivar_name;
+  auto backing_name_it = property_backing_names.find(propAttrs.prop_name);
+  if (backing_name_it != property_backing_names.end())
+    property_ivar_name = backing_name_it->second;
+
   ClangASTMetadata metadata;
   metadata.SetUserID(die.GetID());
   delayed_properties.emplace_back(
       class_clang_type, propAttrs.prop_name,
       member_type->GetLayoutCompilerType(), propAttrs.prop_setter_name,
-      propAttrs.prop_getter_name, propAttrs.prop_attributes, metadata);
+      propAttrs.prop_getter_name, propAttrs.prop_attributes, metadata,
+      property_ivar_name);
 }
 
 llvm::Expected<llvm::APInt> DWARFASTParserClang::ExtractIntFromFormValue(
@@ -3140,12 +3198,16 @@ bool DWARFASTParserClang::ParseChildMembers(
   if (ast == nullptr)
     return false;
 
+  const PropertyBackingStorageNames property_backing_names =
+      ParsePropertyBackingStorageNames(parent_die);
+
   for (DWARFDIE die : parent_die.children()) {
     dw_tag_t tag = die.Tag();
 
     switch (tag) {
     case DW_TAG_APPLE_property:
-      ParseObjCProperty(die, parent_die, class_clang_type, delayed_properties);
+      ParseObjCProperty(die, parent_die, class_clang_type,
+                        property_backing_names, delayed_properties);
       break;
 
     case DW_TAG_variant_part:
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
index 03c431c73fb6ff..1d4569a7588eca 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -14,6 +14,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
 
 #include "DWARFASTParser.h"
 #include "DWARFDIE.h"
@@ -137,6 +138,8 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
   class DelayedAddObjCClassProperty;
   typedef std::vector<DelayedAddObjCClassProperty> DelayedPropertyList;
 
+  typedef llvm::StringMap<llvm::StringRef> PropertyBackingStorageNames;
+
   typedef llvm::DenseMap<
       const lldb_private::plugin::dwarf::DWARFDebugInfoEntry *,
       clang::DeclContext *>
@@ -204,6 +207,9 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
       DelayedPropertyList &delayed_properties,
       lldb_private::ClangASTImporter::LayoutInfo &layout_info);
 
+  PropertyBackingStorageNames ParsePropertyBackingStorageNames(
+      const lldb_private::plugin::dwarf::DWARFDIE &parent_die);
+
   void ParseChildParameters(
       clang::DeclContext *containing_decl_ctx,
       const lldb_private::plugin::dwarf::DWARFDIE &parent_die,
@@ -399,12 +405,15 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
   /// \param parent_die The parent DIE.
   /// \param class_clang_type The Objective-C class that will contain the
   /// created property.
+  /// \param property_backing_names Map from property name to backing member
+  /// name, as produced by ParsePropertyBackingStorageNames.
   /// \param delayed_properties The list of delayed properties that the result
   /// will be appended to.
   void
   ParseObjCProperty(const lldb_private::plugin::dwarf::DWARFDIE &die,
                     const lldb_private::plugin::dwarf::DWARFDIE &parent_die,
                     const lldb_private::CompilerType &class_clang_type,
+                    const PropertyBackingStorageNames &property_backing_names,
                     DelayedPropertyList &delayed_properties);
 
   void
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 5255ec835c0a44..01ff6376b3d0a9 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7090,6 +7090,40 @@ TypeSystemClang::GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type,
   return CompilerType();
 }
 
+llvm::StringRef TypeSystemClang::GetPropertyBackingStorageName(
+    lldb::opaque_compiler_type_t type, llvm::StringRef property_name) {
+  if (!type || property_name.empty())
+    return llvm::StringRef();
+
+  CompilerType compiler_type(weak_from_this(), type);
+
+  CompilerType class_type;
+  if (IsObjCObjectPointerType(compiler_type, &class_type))
+    compiler_type = class_type;
+
+  if (!GetCompleteType(compiler_type.GetOpaqueQualType()))
+    return llvm::StringRef();
+
+  clang::ObjCInterfaceDecl *class_interface_decl =
+      GetAsObjCInterfaceDecl(compiler_type);
+  if (!class_interface_decl)
+    return llvm::StringRef();
+
+  clang::IdentifierInfo &property_ident =
+      getASTContext().Idents.get(property_name);
+  clang::ObjCPropertyDecl *property_decl =
+      class_interface_decl->FindPropertyDeclaration(
+          &property_ident,
+          clang::ObjCPropertyQueryKind::OBJC_PR_query_instance);
+  if (!property_decl)
+    return llvm::StringRef();
+
+  if (clang::ObjCIvarDecl *ivar_decl = property_decl->getPropertyIvarDecl())
+    return ivar_decl->getName();
+
+  return llvm::StringRef();
+}
+
 bool TypeSystemClang::IsTemplateType(lldb::opaque_compiler_type_t type) {
   if (!type)
     return false;
@@ -7931,29 +7965,24 @@ bool TypeSystemClang::AddObjCClassProperty(
 
   CompilerType property_clang_type_to_access;
 
-  if (property_clang_type.IsValid())
-    property_clang_type_to_access = property_clang_type;
-  else if (ivar_decl)
+  if (ivar_decl)
     property_clang_type_to_access = ast->GetType(ivar_decl->getType());
+  else if (property_clang_type.IsValid())
+    property_clang_type_to_access = property_clang_type;
 
   if (!class_interface_decl || !property_clang_type_to_access.IsValid())
     return false;
 
-  clang::TypeSourceInfo *prop_type_source;
-  if (ivar_decl)
-    prop_type_source = clang_ast.getTrivialTypeSourceInfo(ivar_decl->getType());
-  else
-    prop_type_source = clang_ast.getTrivialTypeSourceInfo(
-        ClangUtil::GetQualType(property_clang_type));
+  clang::QualType property_qual_type =
+      ClangUtil::GetQualType(property_clang_type_to_access);
+  clang::TypeSourceInfo *prop_type_source =
+      clang_ast.getTrivialTypeSourceInfo(property_qual_type);
 
   clang::ObjCPropertyDecl *property_decl =
       clang::ObjCPropertyDecl::CreateDeserialized(clang_ast, GlobalDeclID());
   property_decl->setDeclContext(class_interface_decl);
   property_decl->setDeclName(&clang_ast.Idents.get(property_name));
-  property_decl->setType(ivar_decl
-                             ? ivar_decl->getType()
-                             : ClangUtil::GetQualType(property_clang_type),
-                         prop_type_source);
+  property_decl->setType(property_qual_type, prop_type_source);
   SetMemberOwningModule(property_decl, class_interface_decl);
 
   if (!property_decl)
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 28e79f2b0b11f8..cfeac83d3c8398 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -927,6 +927,10 @@ class TypeSystemClang : public TypeSystem {
   CompilerType GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type,
                                            llvm::StringRef name) override;
 
+  llvm::StringRef
+  GetPropertyBackingStorageName(lldb::opaque_compiler_type_t type,
+                                llvm::StringRef property_name) override;
+
   bool IsTemplateType(lldb::opaque_compiler_type_t type) override;
 
   size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type,
diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp
index 6f20bfa89ed38c..e1db240979ac76 100644
--- a/lldb/source/Symbol/CompilerType.cpp
+++ b/lldb/source/Symbol/CompilerType.cpp
@@ -970,6 +970,16 @@ CompilerType::GetDirectNestedTypeWithName(llvm::StringRef name) const {
   return CompilerType();
 }
 
+llvm::StringRef CompilerType::GetPropertyBackingStorageName(
+    llvm::StringRef property_name) const {
+  if (IsValid() && !property_name.empty()) {
+    if (auto type_system_sp = GetTypeSystem())
+      return type_system_sp->GetPropertyBackingStorageName(m_type,
+                                                           property_name);
+  }
+  return llvm::StringRef();
+}
+
 size_t CompilerType::GetNumTemplateArguments(bool expand_pack) const {
   if (IsValid()) {
     if (auto type_system_sp = GetTypeSystem())
diff --git a/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py b/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
index a25a1925272933..a852c93a366b34 100644
--- a/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
+++ b/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
@@ -25,3 +25,24 @@ def test_with_summary(self):
         self.runCmd("type summary add -s 'Parent of ${var._child._name}' 'Parent *'")
         self.expect("dwim-print parent", matching=False, substrs=["_child = 0x"])
         self.expect("dwim-print parent", substrs=['Parent of @"Seven"'])
+
+    @requireDarwin
+    def test_property_backing_storage(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "break here for backing storage", lldb.SBFileSpec("main....
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/225204


More information about the lldb-commits mailing list