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

Piotr Jeremicz via lldb-commits lldb-commits at lists.llvm.org
Sun Oct 4 05:00:20 PDT 2026


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

>From 780ae8ef6d73e058210962fd17de6c0dc32bffa1 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Fri, 11 Sep 2026 14:39:07 +0200
Subject: [PATCH 1/8] [lldb][DWARF] Attach synthesized ivar to Objective-C
 property decls

---
 .../SymbolFile/DWARF/DWARFASTParserClang.cpp  | 72 +++++++++++++++++--
 .../SymbolFile/DWARF/DWARFASTParserClang.h    |  9 +++
 .../TypeSystem/Clang/TypeSystemClang.cpp      | 15 ++--
 3 files changed, 81 insertions(+), 15 deletions(-)

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index b60f1d9e41958..c039aa3234a07 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 03c431c73fb6f..1d4569a7588ec 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 5255ec835c0a4..ac84a66b3a129 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7939,21 +7939,16 @@ bool TypeSystemClang::AddObjCClassProperty(
   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)

>From 37c191ca4cbc041c650ba16e0ebf47ff18bf16f1 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Fri, 11 Sep 2026 22:18:40 +0200
Subject: [PATCH 2/8] [lldb] Add CompilerType::GetPropertyBackingStorageName
 for ObjC properties

---
 lldb/include/lldb/Symbol/CompilerType.h       |  4 +++
 lldb/include/lldb/Symbol/TypeSystem.h         | 12 +++++++
 .../TypeSystem/Clang/TypeSystemClang.cpp      | 35 +++++++++++++++++++
 .../TypeSystem/Clang/TypeSystemClang.h        |  4 +++
 lldb/source/Symbol/CompilerType.cpp           | 10 ++++++
 5 files changed, 65 insertions(+)

diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index 168946cf17475..ffeacd49ffa69 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -485,6 +485,10 @@ class CompilerType {
 
   CompilerType GetDirectNestedTypeWithName(llvm::StringRef name) const;
 
+  /// See TypeSystem::GetPropertyBackingStorageName.
+  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 9ec0f13814975..3c141b9fc0dd3 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -403,6 +403,18 @@ class TypeSystem : public PluginInterface,
     return CompilerType();
   }
 
+  /// Returns the name of the member that backs the named property on
+  /// \a type, if this type system knows of one (e.g. an ivar reached via
+  /// DW_AT_property_forward on a DWARF DW_TAG_property).
+  ///
+  /// \return An empty string if \a type has no property named
+  /// \a property_name, or if that property has no known backing storage.
+  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/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index ac84a66b3a129..2f4b4a6543376 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7090,6 +7090,41 @@ 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);
+
+  // Callers will usually hand us an object pointer (e.g. `Foo *`); strip it
+  // to get at the interface itself.
+  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;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 28e79f2b0b11f..cfeac83d3c839 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 6f20bfa89ed38..8e6f7c03191d5 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())

>From 1912b436ff0717383f593d0da562de1f6936dd22 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 21 Sep 2026 23:00:23 +0200
Subject: [PATCH 3/8] [lldb] Teach dwim-print to resolve ObjC property backing
 storage

---
 .../Commands/CommandObjectDWIMPrint.cpp       | 87 +++++++++++++++++--
 lldb/source/Commands/CommandObjectDWIMPrint.h |  6 ++
 2 files changed, 84 insertions(+), 9 deletions(-)

diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 1b0b4c7881cfc..9df520e4b4089 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,54 @@ 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 +214,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 +252,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 01ba9c225e330..b5e82bb692eac 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;

>From 81b5c6909c49943a317d64c00166abb5e01781c7 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 21 Sep 2026 23:32:16 +0200
Subject: [PATCH 4/8] [lldb] Added DWIM tests

---
 .../dwim-print/objc/TestDWIMPrintObjC.py      | 26 +++++++++++++++++++
 lldb/test/API/commands/dwim-print/objc/main.m | 20 ++++++++++++++
 2 files changed, 46 insertions(+)

diff --git a/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py b/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
index a25a192527293..1ff57a2fd061f 100644
--- a/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
+++ b/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
@@ -25,3 +25,29 @@ 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):
+        """A property backed by a differently-named ivar (DW_TAG_property /
+        DW_AT_property_forward) should take the frame-variable fast path,
+        instead of unconditionally falling back to `expression`. Covers the
+        same three synthesis variants as
+        clang/test/DebugInfo/ObjC/property-backing-storage.m."""
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "break here for backing storage", lldb.SBFileSpec("main.m")
+        )
+        self.runCmd("settings set dwim-print-verbosity full")
+
+        self.expect(
+            "dwim-print obj.declaredBacking",
+            substrs=["ran `frame variable obj._customDeclaredIvar`", "(int) 42"],
+        )
+        self.expect(
+            "dwim-print obj.undeclaredBacking",
+            substrs=["ran `frame variable obj._customUndeclaredIvar`", "(int) 7"],
+        )
+        self.expect(
+            "dwim-print obj.implicitBacking",
+            substrs=["ran `frame variable obj._implicitBacking`", "(int) 99"],
+        )
diff --git a/lldb/test/API/commands/dwim-print/objc/main.m b/lldb/test/API/commands/dwim-print/objc/main.m
index ac7efcf0f07ab..2695a77be060a 100644
--- a/lldb/test/API/commands/dwim-print/objc/main.m
+++ b/lldb/test/API/commands/dwim-print/objc/main.m
@@ -14,11 +14,31 @@ @interface Parent : NSObject
 @implementation Parent
 @end
 
+ at interface C : NSObject {
+  int _customDeclaredIvar;
+}
+ at property int declaredBacking;
+ at property int undeclaredBacking;
+ at property int implicitBacking;
+ at end
+
+ at implementation C
+ at synthesize declaredBacking = _customDeclaredIvar;
+ at synthesize undeclaredBacking = _customUndeclaredIvar;
+// implicitBacking: no @synthesize -> compiler auto-creates _implicitBacking.
+ at end
+
 int main(int argc, char **argv) {
   Child *child = [Child new];
   child.name = @"Seven";
   Parent *parent = [Parent new];
   parent.child = child;
   puts("break here");
+
+  C *obj = [C new];
+  obj.declaredBacking = 42;
+  obj.undeclaredBacking = 7;
+  obj.implicitBacking = 99;
+  puts("break here for backing storage");
   return 0;
 }

>From 17ba9d4862a76fa34b8f4ba57e8f38a1c82ed8a1 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 21 Sep 2026 23:48:11 +0200
Subject: [PATCH 5/8] Removed unnecessary comments

---
 lldb/include/lldb/Symbol/CompilerType.h                  | 1 -
 lldb/include/lldb/Symbol/TypeSystem.h                    | 6 ------
 lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 2 --
 lldb/test/API/commands/dwim-print/objc/main.m            | 1 -
 4 files changed, 10 deletions(-)

diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index ffeacd49ffa69..da22b35a619bf 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -485,7 +485,6 @@ class CompilerType {
 
   CompilerType GetDirectNestedTypeWithName(llvm::StringRef name) const;
 
-  /// See TypeSystem::GetPropertyBackingStorageName.
   llvm::StringRef
   GetPropertyBackingStorageName(llvm::StringRef property_name) const;
 
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 3c141b9fc0dd3..400bb0726a627 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -403,12 +403,6 @@ class TypeSystem : public PluginInterface,
     return CompilerType();
   }
 
-  /// Returns the name of the member that backs the named property on
-  /// \a type, if this type system knows of one (e.g. an ivar reached via
-  /// DW_AT_property_forward on a DWARF DW_TAG_property).
-  ///
-  /// \return An empty string if \a type has no property named
-  /// \a property_name, or if that property has no known backing storage.
   virtual llvm::StringRef
   GetPropertyBackingStorageName(lldb::opaque_compiler_type_t type,
                                 llvm::StringRef property_name) {
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 2f4b4a6543376..b27d69c1fb02a 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7097,8 +7097,6 @@ llvm::StringRef TypeSystemClang::GetPropertyBackingStorageName(
 
   CompilerType compiler_type(weak_from_this(), type);
 
-  // Callers will usually hand us an object pointer (e.g. `Foo *`); strip it
-  // to get at the interface itself.
   CompilerType class_type;
   if (IsObjCObjectPointerType(compiler_type, &class_type))
     compiler_type = class_type;
diff --git a/lldb/test/API/commands/dwim-print/objc/main.m b/lldb/test/API/commands/dwim-print/objc/main.m
index 2695a77be060a..45d39d6a952d4 100644
--- a/lldb/test/API/commands/dwim-print/objc/main.m
+++ b/lldb/test/API/commands/dwim-print/objc/main.m
@@ -25,7 +25,6 @@ @interface C : NSObject {
 @implementation C
 @synthesize declaredBacking = _customDeclaredIvar;
 @synthesize undeclaredBacking = _customUndeclaredIvar;
-// implicitBacking: no @synthesize -> compiler auto-creates _implicitBacking.
 @end
 
 int main(int argc, char **argv) {

>From 50f86db55e43c8a267e66d290feef87cc87ecb3b Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Wed, 23 Sep 2026 18:33:40 +0200
Subject: [PATCH 6/8] [lldb] Restore ivar-type priority in
 AddObjCClassProperty; apply clang-format

AddObjCClassProperty's refactor to a single property_clang_type_to_access
variable silently reversed the type priority it used to have: it now
prefers the property's declared type over the backing ivar's real type
whenever both are available, instead of the other way around. This is
the first branch to ever pass a non-null ivar_decl into this function,
so the ivar-preferring branch had been dead code until now -- the
priority flip went unnoticed because nothing exercised it.

Restore the original priority (ivar's real type wins when known,
falling back to the declared property type otherwise). This only
affects the synthesized ObjCPropertyDecl's static type as seen by full
`expression`/`po` evaluation and type queries -- the dwim-print fast
path itself is unaffected, since it reads the backing ivar directly via
getPropertyIvarDecl()->getName(), not through this type field.

Also applies the clang-format fixes CI flagged on this branch
(CommandObjectDWIMPrint.{h,cpp}, TypeSystemClang.cpp, CompilerType.cpp).

Full lldb/test/API/commands/dwim-print suite passes (60/60).

Co-Authored-By: Claude Sonnet 5 <noreply at anthropic.com>
---
 lldb/source/Commands/CommandObjectDWIMPrint.cpp          | 6 ++----
 lldb/source/Commands/CommandObjectDWIMPrint.h            | 6 +++---
 lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 9 +++++----
 lldb/source/Symbol/CompilerType.cpp                      | 6 +++---
 .../API/commands/dwim-print/objc/TestDWIMPrintObjC.py    | 5 -----
 5 files changed, 13 insertions(+), 19 deletions(-)

diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 9df520e4b4089..7b4be1692ae27 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -73,14 +73,12 @@ std::string CommandObjectDWIMPrint::RewritePathForBackingStorage(
   std::string rewritten_path = components[0].str();
   bool did_rewrite = false;
 
-  for (llvm::StringRef component :
-       llvm::ArrayRef(components).drop_front()) {
+  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);
+          valobj_sp->GetCompilerType().GetPropertyBackingStorageName(component);
       if (backing_name.empty())
         return {};
       child_sp = valobj_sp->GetChildMemberWithName(backing_name);
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.h b/lldb/source/Commands/CommandObjectDWIMPrint.h
index b5e82bb692eac..dd7b9ea7cb77a 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.h
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.h
@@ -44,9 +44,9 @@ 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);
+  std::string RewritePathForBackingStorage(llvm::StringRef expr,
+                                           StackFrame &frame,
+                                           lldb::DynamicValueType use_dynamic);
 
   OptionGroupOptions m_option_group;
   OptionGroupFormat m_format_options = lldb::eFormatDefault;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index b27d69c1fb02a..01ff6376b3d0a 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7113,7 +7113,8 @@ llvm::StringRef TypeSystemClang::GetPropertyBackingStorageName(
       getASTContext().Idents.get(property_name);
   clang::ObjCPropertyDecl *property_decl =
       class_interface_decl->FindPropertyDeclaration(
-          &property_ident, clang::ObjCPropertyQueryKind::OBJC_PR_query_instance);
+          &property_ident,
+          clang::ObjCPropertyQueryKind::OBJC_PR_query_instance);
   if (!property_decl)
     return llvm::StringRef();
 
@@ -7964,10 +7965,10 @@ 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;
diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp
index 8e6f7c03191d5..e1db240979ac7 100644
--- a/lldb/source/Symbol/CompilerType.cpp
+++ b/lldb/source/Symbol/CompilerType.cpp
@@ -970,12 +970,12 @@ CompilerType::GetDirectNestedTypeWithName(llvm::StringRef name) const {
   return CompilerType();
 }
 
-llvm::StringRef
-CompilerType::GetPropertyBackingStorageName(llvm::StringRef property_name) const {
+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);
+                                                           property_name);
   }
   return llvm::StringRef();
 }
diff --git a/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py b/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
index 1ff57a2fd061f..a852c93a366b3 100644
--- a/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
+++ b/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
@@ -28,11 +28,6 @@ def test_with_summary(self):
 
     @requireDarwin
     def test_property_backing_storage(self):
-        """A property backed by a differently-named ivar (DW_TAG_property /
-        DW_AT_property_forward) should take the frame-variable fast path,
-        instead of unconditionally falling back to `expression`. Covers the
-        same three synthesis variants as
-        clang/test/DebugInfo/ObjC/property-backing-storage.m."""
         self.build()
         lldbutil.run_to_source_breakpoint(
             self, "break here for backing storage", lldb.SBFileSpec("main.m")

>From bb9f62e200ea3486f4c2d252d1ff19f69fa8d763 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Sun, 4 Oct 2026 11:20:47 +0200
Subject: [PATCH 7/8] [lldb] Document backing-storage helpers per review
 feedback

---
 lldb/include/lldb/Symbol/CompilerType.h              |  1 +
 lldb/include/lldb/Symbol/TypeSystem.h                |  6 ++++++
 lldb/source/Commands/CommandObjectDWIMPrint.h        | 12 ++++++++++++
 .../Plugins/SymbolFile/DWARF/DWARFASTParserClang.h   |  4 ++++
 4 files changed, 23 insertions(+)

diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index da22b35a619bf..ffeacd49ffa69 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -485,6 +485,7 @@ class CompilerType {
 
   CompilerType GetDirectNestedTypeWithName(llvm::StringRef name) const;
 
+  /// See TypeSystem::GetPropertyBackingStorageName.
   llvm::StringRef
   GetPropertyBackingStorageName(llvm::StringRef property_name) const;
 
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 400bb0726a627..3c141b9fc0dd3 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();
   }
 
+  /// Returns the name of the member that backs the named property on
+  /// \a type, if this type system knows of one (e.g. an ivar reached via
+  /// DW_AT_property_forward on a DWARF DW_TAG_property).
+  ///
+  /// \return An empty string if \a type has no property named
+  /// \a property_name, or if that property has no known backing storage.
   virtual llvm::StringRef
   GetPropertyBackingStorageName(lldb::opaque_compiler_type_t type,
                                 llvm::StringRef property_name) {
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.h b/lldb/source/Commands/CommandObjectDWIMPrint.h
index dd7b9ea7cb77a..c57c5292857f2 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.h
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.h
@@ -44,6 +44,18 @@ class CommandObjectDWIMPrint : public CommandObjectRaw {
 private:
   void DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
 
+  /// Rewrites a dotted expression path (e.g. `parent.child.name`) into one
+  /// that names backing storage members directly, for components that
+  /// resolve to a synthesized property rather than a real member.
+  ///
+  /// Walks the path component by component, starting from \a expr's first
+  /// component resolved as a frame variable. For each subsequent component,
+  /// a plain child lookup is tried first; only if that fails is the type
+  /// asked for the property's backing storage name, and the lookup retried
+  /// with that name.
+  ///
+  /// \return The rewritten path, or an empty string if nothing needed
+  /// rewriting (including if \a expr could not be fully resolved at all).
   std::string RewritePathForBackingStorage(llvm::StringRef expr,
                                            StackFrame &frame,
                                            lldb::DynamicValueType use_dynamic);
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
index 1d4569a7588ec..aa2de903f16ed 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -207,6 +207,10 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
       DelayedPropertyList &delayed_properties,
       lldb_private::ClangASTImporter::LayoutInfo &layout_info);
 
+  /// Scans \a parent_die's DW_TAG_property children for a
+  /// DW_TAG_property_getter with a DW_AT_property_forward attribute, and
+  /// returns a map from each such property's name to the name of the member
+  /// it forwards to (its backing storage).
   PropertyBackingStorageNames ParsePropertyBackingStorageNames(
       const lldb_private::plugin::dwarf::DWARFDIE &parent_die);
 

>From fa8ad967e4d9f20146dbbd75b957716f40877b40 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Sun, 4 Oct 2026 14:00:03 +0200
Subject: [PATCH 8/8] [lldb] Return errors from RewritePathForBackingStorage;
 drop typedef

---
 .../Commands/CommandObjectDWIMPrint.cpp       | 32 +++++++++++++------
 lldb/source/Commands/CommandObjectDWIMPrint.h | 12 ++++---
 .../SymbolFile/DWARF/DWARFASTParserClang.cpp  |  8 ++---
 .../SymbolFile/DWARF/DWARFASTParserClang.h    | 16 ++++------
 4 files changed, 40 insertions(+), 28 deletions(-)

diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 7b4be1692ae27..c5c7df9c7e4a1 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -18,12 +18,15 @@
 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
 #include "lldb/Target/StackFrame.h"
 #include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
 #include "lldb/ValueObject/ValueObject.h"
 #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 "llvm/Support/Error.h"
 
 #include <regex>
 
@@ -52,13 +55,12 @@ CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter &interpreter)
 
 Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; }
 
-std::string CommandObjectDWIMPrint::RewritePathForBackingStorage(
+llvm::Expected<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;
@@ -68,7 +70,8 @@ std::string CommandObjectDWIMPrint::RewritePathForBackingStorage(
           StackFrame::eExpressionPathOptionsDisallowGlobals,
       var_sp, status, lldb::eDILModeSimple);
   if (!valobj_sp || !status.Success() || valobj_sp->GetError().Fail())
-    return {};
+    return llvm::createStringError("could not resolve '" + components[0] +
+                                   "' as a variable in the current frame");
 
   std::string rewritten_path = components[0].str();
   bool did_rewrite = false;
@@ -80,10 +83,14 @@ std::string CommandObjectDWIMPrint::RewritePathForBackingStorage(
       llvm::StringRef backing_name =
           valobj_sp->GetCompilerType().GetPropertyBackingStorageName(component);
       if (backing_name.empty())
-        return {};
+        return llvm::createStringError(
+            "'" + component + "' is neither a member of '" + rewritten_path +
+            "' nor a property with known backing storage");
       child_sp = valobj_sp->GetChildMemberWithName(backing_name);
       if (!child_sp)
-        return {};
+        return llvm::createStringError(
+            "backing storage '" + backing_name + "' of property '" + component +
+            "' is not a member of '" + rewritten_path + "'");
       name_used = backing_name;
       did_rewrite = true;
     }
@@ -93,7 +100,9 @@ std::string CommandObjectDWIMPrint::RewritePathForBackingStorage(
   }
 
   if (!did_rewrite)
-    return {};
+    return llvm::createStringError("no component of '" + expr +
+                                   "' is a property backed by a differently "
+                                   "named member");
 
   return rewritten_path;
 }
@@ -230,12 +239,15 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
 
     std::string rewritten_path;
     if (!valobj_sp) {
-      rewritten_path = RewritePathForBackingStorage(
-          expr, *frame, eval_options.GetUseDynamic());
-      if (!rewritten_path.empty()) {
+      if (llvm::Expected<std::string> rewritten = RewritePathForBackingStorage(
+              expr, *frame, eval_options.GetUseDynamic())) {
+        rewritten_path = std::move(*rewritten);
         valobj_sp = try_variable_expr(rewritten_path);
         if (valobj_sp)
           used_path = rewritten_path;
+      } else {
+        LLDB_LOG_ERROR(GetLog(LLDBLog::Commands), rewritten.takeError(),
+                       "dwim-print: not rewriting '{1}': {0}", expr);
       }
     }
 
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.h b/lldb/source/Commands/CommandObjectDWIMPrint.h
index c57c5292857f2..58b68a7ada6f1 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.h
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.h
@@ -14,6 +14,7 @@
 #include "lldb/Interpreter/OptionGroupFormat.h"
 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
 #include "lldb/Interpreter/OptionValueFormat.h"
+#include "llvm/Support/Error.h"
 
 namespace lldb_private {
 
@@ -54,11 +55,12 @@ class CommandObjectDWIMPrint : public CommandObjectRaw {
   /// asked for the property's backing storage name, and the lookup retried
   /// with that name.
   ///
-  /// \return The rewritten path, or an empty string if nothing needed
-  /// rewriting (including if \a expr could not be fully resolved at all).
-  std::string RewritePathForBackingStorage(llvm::StringRef expr,
-                                           StackFrame &frame,
-                                           lldb::DynamicValueType use_dynamic);
+  /// \return The rewritten path, or an error describing why \a expr could
+  /// not be rewritten (e.g. it could not be resolved, or nothing needed
+  /// rewriting).
+  llvm::Expected<std::string>
+  RewritePathForBackingStorage(llvm::StringRef expr, StackFrame &frame,
+                               lldb::DynamicValueType use_dynamic);
 
   OptionGroupOptions m_option_group;
   OptionGroupFormat m_format_options = lldb::eFormatDefault;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index c039aa3234a07..4ddc1b7d7550c 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -2853,10 +2853,10 @@ PropertyAttributes::PropertyAttributes(const DWARFDIE &die) {
   }
 }
 
-DWARFASTParserClang::PropertyBackingStorageNames
+llvm::StringMap<llvm::StringRef>
 DWARFASTParserClang::ParsePropertyBackingStorageNames(
     const DWARFDIE &parent_die) {
-  PropertyBackingStorageNames property_backing_names;
+  llvm::StringMap<llvm::StringRef> property_backing_names;
 
   for (DWARFDIE die : parent_die.children()) {
     if (die.Tag() != DW_TAG_property)
@@ -2885,7 +2885,7 @@ DWARFASTParserClang::ParsePropertyBackingStorageNames(
 void DWARFASTParserClang::ParseObjCProperty(
     const DWARFDIE &die, const DWARFDIE &parent_die,
     const lldb_private::CompilerType &class_clang_type,
-    const PropertyBackingStorageNames &property_backing_names,
+    const llvm::StringMap<llvm::StringRef> &property_backing_names,
     DelayedPropertyList &delayed_properties) {
   // This function can only parse DW_TAG_APPLE_property.
   assert(die.Tag() == DW_TAG_APPLE_property);
@@ -3198,7 +3198,7 @@ bool DWARFASTParserClang::ParseChildMembers(
   if (ast == nullptr)
     return false;
 
-  const PropertyBackingStorageNames property_backing_names =
+  const llvm::StringMap<llvm::StringRef> property_backing_names =
       ParsePropertyBackingStorageNames(parent_die);
 
   for (DWARFDIE die : parent_die.children()) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
index aa2de903f16ed..21df769a3167d 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -138,8 +138,6 @@ 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 *>
@@ -211,7 +209,7 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
   /// DW_TAG_property_getter with a DW_AT_property_forward attribute, and
   /// returns a map from each such property's name to the name of the member
   /// it forwards to (its backing storage).
-  PropertyBackingStorageNames ParsePropertyBackingStorageNames(
+  llvm::StringMap<llvm::StringRef> ParsePropertyBackingStorageNames(
       const lldb_private::plugin::dwarf::DWARFDIE &parent_die);
 
   void ParseChildParameters(
@@ -413,12 +411,12 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
   /// 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 ParseObjCProperty(
+      const lldb_private::plugin::dwarf::DWARFDIE &die,
+      const lldb_private::plugin::dwarf::DWARFDIE &parent_die,
+      const lldb_private::CompilerType &class_clang_type,
+      const llvm::StringMap<llvm::StringRef> &property_backing_names,
+      DelayedPropertyList &delayed_properties);
 
   void
   ParseSingleMember(const lldb_private::plugin::dwarf::DWARFDIE &die,



More information about the lldb-commits mailing list