Skip to content

[LLDB] Add field member operators to DIL #138093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

cmtice
Copy link
Contributor

@cmtice cmtice commented May 1, 2025

Add the arrow and period operators, allowing DIL to find and access member fields.

Add the arrow and period operators, allowing DIL to find and access
member fields.
@cmtice cmtice requested a review from JDevlieghere as a code owner May 1, 2025 07:08
@llvmbot llvmbot added the lldb label May 1, 2025
@cmtice cmtice requested review from labath and removed request for JDevlieghere May 1, 2025 07:08
@cmtice cmtice self-assigned this May 1, 2025
@llvmbot
Copy link
Member

llvmbot commented May 1, 2025

@llvm/pr-subscribers-lldb

Author: None (cmtice)

Changes

Add the arrow and period operators, allowing DIL to find and access member fields.


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

18 Files Affected:

  • (modified) lldb/docs/dil-expr-lang.ebnf (+7-3)
  • (modified) lldb/include/lldb/ValueObject/DILAST.h (+26)
  • (modified) lldb/include/lldb/ValueObject/DILEval.h (+8)
  • (modified) lldb/include/lldb/ValueObject/DILLexer.h (+2)
  • (modified) lldb/include/lldb/ValueObject/DILParser.h (+1)
  • (modified) lldb/source/ValueObject/DILAST.cpp (+4)
  • (modified) lldb/source/ValueObject/DILEval.cpp (+200)
  • (modified) lldb/source/ValueObject/DILLexer.cpp (+7-2)
  • (modified) lldb/source/ValueObject/DILParser.cpp (+24-3)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile (+3)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py (+47)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp (+59)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile (+3)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py (+62)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp (+74)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile (+3)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py (+49)
  • (added) lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp (+87)
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index c8bf4231b3e4a..580626862c005 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -5,13 +5,17 @@
 
 expression = unary_expression ;
 
-unary_expression = unary_operator expression
-                 | primary_expression ;
+unary_expression = postfix_expression
+                 | unary_operator expression ;
 
 unary_operator = "*" | "&" ;
 
+postfix_expresson = primary_expression
+                  | postfix_expression "." id_expression
+                  | postfix_expression "->" id_expression ;
+
 primary_expression = id_expression
-                   | "(" expression ")";
+                   | "(" expression ")"  ;
 
 id_expression = unqualified_id
               | qualified_id
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index fe3827ef0516a..a74a12bd8be9d 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -20,6 +20,7 @@ namespace lldb_private::dil {
 enum class NodeKind {
   eErrorNode,
   eIdentifierNode,
+  eMemberOfNode,
   eUnaryOpNode,
 };
 
@@ -88,6 +89,29 @@ class IdentifierNode : public ASTNode {
   std::string m_name;
 };
 
+class MemberOfNode : public ASTNode {
+public:
+  MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow,
+               ConstString name)
+      : ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
+        m_is_arrow(is_arrow), m_field_name(name) { }
+
+  llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+  ASTNode *base() const { return m_base.get(); }
+  bool IsArrow() const { return m_is_arrow; }
+  ConstString FieldName() const { return m_field_name; }
+
+  static bool classof(const ASTNode *node) {
+    return node->GetKind() == NodeKind::eMemberOfNode;
+  }
+
+private:
+  ASTNodeUP m_base;
+  bool m_is_arrow;
+  ConstString m_field_name;
+};
+
 class UnaryOpNode : public ASTNode {
 public:
   UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP operand)
@@ -118,6 +142,8 @@ class Visitor {
   virtual llvm::Expected<lldb::ValueObjectSP>
   Visit(const IdentifierNode *node) = 0;
   virtual llvm::Expected<lldb::ValueObjectSP>
+  Visit(const MemberOfNode *node) = 0;
+  virtual llvm::Expected<lldb::ValueObjectSP>
   Visit(const UnaryOpNode *node) = 0;
 };
 
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index b1dd3fdb49739..053daffaa41f2 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -49,8 +49,16 @@ class Interpreter : Visitor {
 private:
   llvm::Expected<lldb::ValueObjectSP>
   Visit(const IdentifierNode *node) override;
+  llvm::Expected<lldb::ValueObjectSP> Visit(const MemberOfNode *node) override;
   llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
 
+  lldb::ValueObjectSP EvaluateMemberOf(lldb::ValueObjectSP value,
+                                       const std::vector<uint32_t> &path,
+                                       bool use_synthetic, bool is_dynamic);
+
+  lldb::ValueObjectSP FindMemberWithName(lldb::ValueObjectSP base,
+                                         ConstString name, bool is_arrow);
+
   // Used by the interpreter to create objects, perform casts, etc.
   lldb::TargetSP m_target;
   llvm::StringRef m_expr;
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index 3508b8b7a85c6..9475519a43d2a 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -25,10 +25,12 @@ class Token {
 public:
   enum Kind {
     amp,
+    arrow,
     coloncolon,
     eof,
     identifier,
     l_paren,
+    period,
     r_paren,
     star,
   };
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index f5c00b1040ef4..c62f8908290f5 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -84,6 +84,7 @@ class DILParser {
 
   ASTNodeUP ParseExpression();
   ASTNodeUP ParseUnaryExpression();
+  ASTNodeUP ParsePostfixExpression();
   ASTNodeUP ParsePrimaryExpression();
 
   std::string ParseNestedNameSpecifier();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index ea847587501ee..2814d96b94e6e 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -19,6 +19,10 @@ llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
   return v->Visit(this);
 }
 
+llvm::Expected<lldb::ValueObjectSP> MemberOfNode::Accept(Visitor *v) const {
+  return v->Visit(this);
+}
+
 llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const {
   return v->Visit(this);
 }
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 15a66d4866305..1f1ad7161f42e 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -272,4 +272,204 @@ Interpreter::Visit(const UnaryOpNode *node) {
       m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
 }
 
+lldb::ValueObjectSP
+Interpreter::EvaluateMemberOf(lldb::ValueObjectSP value,
+                              const std::vector<uint32_t> &path,
+                              bool use_synthetic, bool is_dynamic) {
+  lldb::ValueObjectSP member_val_sp = value;
+
+  lldb::DynamicValueType use_dynamic =
+      (!is_dynamic) ? lldb::eNoDynamicValues : lldb::eDynamicDontRunTarget;
+  // Walk the path from the base value to the value that contains the requested field.
+  for (uint32_t idx : path) {
+    member_val_sp = member_val_sp->GetChildAtIndex(idx, /*can_create*/ true);
+  }
+  // If that didn't work, try it with the dynamic value.
+  if (!member_val_sp && is_dynamic) {
+    lldb::ValueObjectSP dyn_val_sp = value->GetDynamicValue(use_dynamic);
+    if (dyn_val_sp) {
+      for (uint32_t idx : path) {
+        dyn_val_sp = dyn_val_sp->GetChildAtIndex(idx, true);
+      }
+      member_val_sp = dyn_val_sp;
+    }
+  }
+  assert(member_val_sp && "invalid ast: invalid member access");
+
+  return member_val_sp;
+}
+
+static bool GetFieldIndex(CompilerType type, const std::string &name,
+                          std::vector<uint32_t> *idx_path) {
+  bool found = false;
+  uint32_t num_fields = type.GetNumFields();
+  for (uint32_t i = 0; i < num_fields; ++i) {
+    uint64_t bit_offset = 0;
+    uint32_t bitfield_bit_size = 0;
+    bool is_bitfield = false;
+    std::string name_sstr;
+    CompilerType field_type(type.GetFieldAtIndex(
+        i, name_sstr, &bit_offset, &bitfield_bit_size, &is_bitfield));
+    auto field_name =
+        name_sstr.length() == 0 ? std::optional<std::string>() : name_sstr;
+    if (field_type.IsValid() && name_sstr == name) {
+      idx_path->push_back(i + type.GetNumberOfNonEmptyBaseClasses());
+      found = true;
+      break;
+    } else if (field_type.IsAnonymousType()) {
+      found = GetFieldIndex(field_type, name, idx_path);
+      if (found) {
+        idx_path->push_back(i + type.GetNumberOfNonEmptyBaseClasses());
+        break;
+      }
+    }
+  }
+  return found;
+}
+
+static bool SearchBaseClassesForField(lldb::ValueObjectSP base_sp,
+                                      CompilerType base_type,
+                                      const std::string &name,
+                                      std::vector<uint32_t> *idx_path,
+                                      bool use_synthetic, bool is_dynamic) {
+  bool found = false;
+  uint32_t num_non_empty_bases = 0;
+  uint32_t num_direct_bases = base_type.GetNumDirectBaseClasses();
+  for (uint32_t i = 0; i < num_direct_bases; ++i) {
+    uint32_t bit_offset;
+    CompilerType base_class =
+        base_type.GetDirectBaseClassAtIndex(i, &bit_offset);
+    std::vector<uint32_t> field_idx_path;
+    if (GetFieldIndex(base_class, name, &field_idx_path)) {
+      for (uint32_t j : field_idx_path)
+        idx_path->push_back(j + base_class.GetNumberOfNonEmptyBaseClasses());
+      idx_path->push_back(i);
+      return true;
+    }
+
+    found = SearchBaseClassesForField(base_sp, base_class, name, idx_path,
+                                      use_synthetic, is_dynamic);
+    if (found) {
+      idx_path->push_back(i);
+      return true;
+    }
+
+    if (base_class.GetNumFields() > 0)
+      num_non_empty_bases += 1;
+  }
+  return false;
+}
+
+lldb::ValueObjectSP Interpreter::FindMemberWithName(lldb::ValueObjectSP base,
+                                                    ConstString name,
+                                                    bool is_arrow) {
+  bool is_synthetic = false;
+  bool is_dynamic = true;
+  // See if GetChildMemberWithName works.
+  lldb::ValueObjectSP field_obj =
+      base->GetChildMemberWithName(name.GetStringRef());
+  if (field_obj && field_obj->GetName() == name)
+    return field_obj;
+
+  // Check for synthetic member.
+  lldb::ValueObjectSP child_sp = base->GetSyntheticValue();
+  if (child_sp) {
+    is_synthetic = true;
+    field_obj = child_sp->GetChildMemberWithName(name);
+    if (field_obj && field_obj->GetName() == name)
+      return field_obj;
+  }
+
+  // Check indices of immediate member fields of base's type.
+  CompilerType base_type = base->GetCompilerType();
+  std::vector<uint32_t> field_idx_path;
+  if (GetFieldIndex(base_type, name.GetString(), &field_idx_path)) {
+    std::reverse(field_idx_path.begin(), field_idx_path.end());
+    // Traverse the path & verify the final object is correct.
+    field_obj = base;
+    for (uint32_t i : field_idx_path)
+      field_obj = field_obj->GetChildAtIndex(i, true);
+    if (field_obj && field_obj->GetName() == name)
+      return field_obj;
+  }
+
+  // Go through base classes and look for field there.
+  std::vector<uint32_t> base_class_idx_path;
+  bool found =
+      SearchBaseClassesForField(base, base_type, name.GetString(),
+                                &base_class_idx_path, is_synthetic, is_dynamic);
+  if (found && !base_class_idx_path.empty()) {
+    std::reverse(base_class_idx_path.begin(), base_class_idx_path.end());
+    field_obj =
+        EvaluateMemberOf(base, base_class_idx_path, is_synthetic, is_dynamic);
+    if (field_obj && field_obj->GetName() == name)
+      return field_obj;
+  }
+
+  // Field not found.
+  return lldb::ValueObjectSP();
+}
+
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const MemberOfNode *node) {
+  Status error;
+  auto base_or_err = Evaluate(node->base());
+  if (!base_or_err) {
+    return base_or_err;
+  }
+  lldb::ValueObjectSP base = *base_or_err;
+
+  // Perform basic type checking.
+  CompilerType base_type = base->GetCompilerType();
+  // When using an arrow, make sure the base is a pointer or array type.
+  // When using a period, make sure the base type is NOT a pointer type.
+  if (node->IsArrow() && !base_type.IsPointerType() &&
+      !base_type.IsArrayType()) {
+    lldb::ValueObjectSP deref_sp = base->Dereference(error);
+    if (error.Success()) {
+      base = deref_sp;
+      base_type = deref_sp->GetCompilerType().GetPointerType();
+    } else {
+      std::string errMsg =
+          llvm::formatv("member reference type {0} is not a pointer; "
+                        "did you mean to use '.'?",
+                        base_type.TypeDescription());
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, errMsg, node->GetLocation(), node->FieldName().GetLength());
+    }
+  } else if (!node->IsArrow() && base_type.IsPointerType()) {
+    std::string errMsg =
+        llvm::formatv("member reference type {0} is a pointer; "
+                      "did you mean to use '->'?",
+                      base_type.TypeDescription());
+    return llvm::make_error<DILDiagnosticError>(
+        m_expr, errMsg, node->GetLocation(), node->FieldName().GetLength());
+  }
+
+  // User specified array->elem; need to get to element[0] to look for fields.
+  if (node->IsArrow() && base_type.IsArrayType())
+    base = base->GetChildAtIndex(0);
+
+  // Now look for the member with the specified name.
+  lldb::ValueObjectSP field_obj =
+      FindMemberWithName(base, node->FieldName(), node->IsArrow());
+  if (field_obj) {
+    if (field_obj->GetCompilerType().IsReferenceType()) {
+      lldb::ValueObjectSP tmp_obj = field_obj->Dereference(error);
+      if (error.Fail())
+        return error.ToError();
+      return tmp_obj;
+    }
+    return field_obj;
+  }
+
+  if (node->IsArrow() && base_type.IsPointerType())
+    base_type = base_type.GetPointeeType();
+  std::string errMsg = llvm::formatv(
+      "no member named '{0}' in {1}", node->FieldName().GetStringRef(),
+      base_type.GetFullyUnqualifiedType().TypeDescription());
+  return llvm::make_error<DILDiagnosticError>(
+      m_expr, errMsg, node->GetLocation(), node->FieldName().GetLength());
+}
+
 } // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index b9c2e7971e3b4..1610b4e87d7fb 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -21,6 +21,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
   switch (kind) {
   case Kind::amp:
     return "amp";
+  case Kind::arrow:
+    return "arrow";
   case Kind::coloncolon:
     return "coloncolon";
   case Kind::eof:
@@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
     return "identifier";
   case Kind::l_paren:
     return "l_paren";
+  case Kind::period:
+    return "period";
   case Kind::r_paren:
     return "r_paren";
   case Token::star:
@@ -86,8 +90,9 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
     return Token(Token::identifier, maybe_word->str(), position);
 
   constexpr std::pair<Token::Kind, const char *> operators[] = {
-      {Token::amp, "&"},     {Token::coloncolon, "::"}, {Token::l_paren, "("},
-      {Token::r_paren, ")"}, {Token::star, "*"},
+      {Token::amp, "&"},     {Token::arrow, "->"}, {Token::coloncolon, "::"},
+      {Token::l_paren, "("}, {Token::period, "."}, {Token::r_paren, ")"},
+      {Token::star, "*"},
   };
   for (auto [kind, str] : operators) {
     if (remainder.consume_front(str))
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 2c78eae8cf6bf..9c5bc71775fb2 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -79,15 +79,15 @@ ASTNodeUP DILParser::Run() {
 // Parse an expression.
 //
 //  expression:
-//    primary_expression
+//    unary_expression
 //
 ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
 
 // Parse an unary_expression.
 //
 //  unary_expression:
+//    postfix_expression
 //    unary_operator expression
-//    primary_expression
 //
 //  unary_operator:
 //    "&"
@@ -111,7 +111,28 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
       llvm_unreachable("invalid token kind");
     }
   }
-  return ParsePrimaryExpression();
+  return ParsePostfixExpression();
+}
+// Parse a postfix_expression.
+//
+//  postfix_expression:
+//    primary_expression
+//    postfix_expression "." id_expression
+//    postfix_expression "->" id_expression
+//
+ASTNodeUP DILParser::ParsePostfixExpression() {
+  ASTNodeUP lhs = ParsePrimaryExpression();
+  while (CurToken().IsOneOf({Token::period, Token::arrow})) {
+    Token token = CurToken();
+    m_dil_lexer.Advance();
+    Token member_token = CurToken();
+    std::string member_id = ParseIdExpression();
+    lhs = std::make_unique<MemberOfNode>(member_token.GetLocation(),
+                                         std::move(lhs),
+                                         token.GetKind() == Token::arrow,
+                                         ConstString(member_id));
+  }
+  return lhs;
 }
 
 // Parse a primary_expression.
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
new file mode 100644
index 0000000000000..ff942c88bf183
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
@@ -0,0 +1,47 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+class TestFrameVarDILMemberOf(TestBase):
+    # If your test case doesn't stress debug info, then
+    # set this to true.  That way it won't be run once for
+    # each debug info format.
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_frame_var(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
+                                          lldb.SBFileSpec("main.cpp"))
+
+        self.expect("settings set target.experimental.use-DIL true",
+                    substrs=[""])
+        self.expect_var_path("s.x", value="1")
+        self.expect_var_path("s.r", value="2")
+        self.expect_var_path("sr.x", value="1")
+        self.expect_var_path("sr.r", value="2")
+        self.expect_var_path("sp->x", value="1")
+        self.expect_var_path("sp->r", value="2")
+        self.expect_var_path("sarr->x", value="5");
+        self.expect_var_path("sarr->r", value="2")
+
+        self.expect("frame variable 'sp->foo'", error=True,
+                    substrs=["no member named 'foo' in 'Sx'"])
+
+        self.expect("frame variable 'sp.x'", error=True,
+                    substrs=["member reference type 'Sx *' is a "
+                             "pointer; did you mean to use '->'"])
+        self.expect("frame variable 'sarr.x'", error=True,
+                    substrs=["no member named 'x' in 'Sx[2]'"])
+
+        # Test for record typedefs.
+        self.expect_var_path("sa.x", value="3")
+        self.expect_var_path("sa.y", value="'\\x04'")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
new file mode 100644
index 0000000000000..dace888bef4dc
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
@@ -0,0 +1,59 @@
+int
+main(int argc, char**argv)
+{
+  int x = 2;
+  struct Sx {
+    int x;
+    int& r;
+    char y;
+  } s{1, x, 2};
+
+  Sx& sr = s;
+  Sx* sp = &s;
+
+  Sx sarr[2] = {{5, x, 2}, {1, x, 3}};
+
+  using SxAlias = Sx;
+  SxAlias sa{3, x, 4};
+
+  return 0; // Set a breakpoint here
+}
+
+/*
+  EXPECT_THAT(Eval("s.x"), IsEqual("1"));
+  EXPECT_THAT(Eval("s.r"), IsEqual("2"));
+  EXPECT_THAT(Eval("s.r + 1"), IsEqual("3"));
+  EXPECT_THAT(Eval("sr.x"), IsEqual("1"));
+  EXPECT_THAT(Eval("sr.r"), IsEqual("2"));
+  EXPECT_THAT(Eval("sr.r + 1"), IsEqual("3"));
+  EXPECT_THAT(Eval("sp->x"), IsEqual("1"));
+  EXPECT_THAT(Eval("sp->r"), IsEqual("2"));
+  EXPECT_THAT(Eval("sp->r + 1"), IsEqual("3"));
+  EXPECT_THAT(Eval("sarr->x"), IsEqual("5"));
+  EXPECT_THAT(Eval("sarr->r"), IsEqual("2"));
+  EXPECT_THAT(Eval("sarr->r + 1"), IsEqual("3"));
+  EXPECT_THAT(Eval("(sarr + 1)->x"), IsEqual("1"));
+
+  EXPECT_THAT(
+      Eval("sp->4"),
+      IsError(
+          "<expr>:1:5: expected 'identifier', got: <'4' (numeric_constant)>\n"
+          "sp->4\n"
+          "    ^"));
+  EXPECT_THAT(Eval("sp->foo"), IsError("no member named 'foo' in 'Sx'"));
+  EXPECT_THAT(
+      Eval("sp->r / (void*)0"),
+      IsError("invalid operands to binary expression ('int' and 'void *')"));
+
+  EXPECT_THAT(Eval("sp.x"), IsError("member reference type 'Sx *' is a "
+                                    "pointer; did you mean to use '->'"));
+  EXPECT_THAT(
+      Eval("sarr.x"),
+      IsError(
+          "member reference base type 'Sx[2]' is not a structure or union"));
+
+  // Test for record typedefs.
+  EXPECT_THAT(Eval("sa.x"), IsEqual("3"));
+  EXPECT_THAT(Eval("sa.y"), IsEqual("'\\x04'"));
+
+*/
diff --git a/lldb/test/API/commands/frame/var-dil...
[truncated]

Copy link

github-actions bot commented May 1, 2025

⚠️ Python code formatter, darker found issues in your code. ⚠️

You can test this locally with the following command:
darker --check --diff -r HEAD~1...HEAD lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py
View the diff from darker here.
--- MemberOf/TestFrameVarDILMemberOf.py	2025-05-01 07:05:57.000000 +0000
+++ MemberOf/TestFrameVarDILMemberOf.py	2025-05-01 07:10:01.907042 +0000
@@ -9,39 +9,50 @@
 
 import os
 import shutil
 import time
 
+
 class TestFrameVarDILMemberOf(TestBase):
     # If your test case doesn't stress debug info, then
     # set this to true.  That way it won't be run once for
     # each debug info format.
     NO_DEBUG_INFO_TESTCASE = True
 
     def test_frame_var(self):
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
-                                          lldb.SBFileSpec("main.cpp"))
+        lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
 
-        self.expect("settings set target.experimental.use-DIL true",
-                    substrs=[""])
+        self.expect("settings set target.experimental.use-DIL true", substrs=[""])
         self.expect_var_path("s.x", value="1")
         self.expect_var_path("s.r", value="2")
         self.expect_var_path("sr.x", value="1")
         self.expect_var_path("sr.r", value="2")
         self.expect_var_path("sp->x", value="1")
         self.expect_var_path("sp->r", value="2")
-        self.expect_var_path("sarr->x", value="5");
+        self.expect_var_path("sarr->x", value="5")
         self.expect_var_path("sarr->r", value="2")
 
-        self.expect("frame variable 'sp->foo'", error=True,
-                    substrs=["no member named 'foo' in 'Sx'"])
+        self.expect(
+            "frame variable 'sp->foo'",
+            error=True,
+            substrs=["no member named 'foo' in 'Sx'"],
+        )
 
-        self.expect("frame variable 'sp.x'", error=True,
-                    substrs=["member reference type 'Sx *' is a "
-                             "pointer; did you mean to use '->'"])
-        self.expect("frame variable 'sarr.x'", error=True,
-                    substrs=["no member named 'x' in 'Sx[2]'"])
+        self.expect(
+            "frame variable 'sp.x'",
+            error=True,
+            substrs=[
+                "member reference type 'Sx *' is a " "pointer; did you mean to use '->'"
+            ],
+        )
+        self.expect(
+            "frame variable 'sarr.x'",
+            error=True,
+            substrs=["no member named 'x' in 'Sx[2]'"],
+        )
 
         # Test for record typedefs.
         self.expect_var_path("sa.x", value="3")
         self.expect_var_path("sa.y", value="'\\x04'")
--- MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py	2025-05-01 07:05:57.000000 +0000
+++ MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py	2025-05-01 07:10:01.933926 +0000
@@ -9,29 +9,31 @@
 
 import os
 import shutil
 import time
 
+
 class TestFrameVarDILMemberOfAnonymousMember(TestBase):
     # If your test case doesn't stress debug info, then
     # set this to true.  That way it won't be run once for
     # each debug info format.
     NO_DEBUG_INFO_TESTCASE = True
 
     def test_frame_var(self):
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
-                                          lldb.SBFileSpec("main.cpp"))
+        lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
 
-        self.expect("settings set target.experimental.use-DIL true",
-                    substrs=[""])
+        self.expect("settings set target.experimental.use-DIL true", substrs=[""])
         self.expect_var_path("a.x", value="1")
         self.expect_var_path("a.y", value="2")
 
-        self.expect("frame variable 'b.x'", error=True,
-                    substrs=["no member named 'x' in 'B'"])
-        #self.expect_var_path("b.y", value="0")
+        self.expect(
+            "frame variable 'b.x'", error=True, substrs=["no member named 'x' in 'B'"]
+        )
+        # self.expect_var_path("b.y", value="0")
         self.expect_var_path("b.z", value="3")
         self.expect_var_path("b.w", value="4")
         self.expect_var_path("b.a.x", value="1")
         self.expect_var_path("b.a.y", value="2")
 
@@ -41,22 +43,30 @@
         self.expect_var_path("d.x", value="7")
         self.expect_var_path("d.y", value="8")
         self.expect_var_path("d.z", value="9")
         self.expect_var_path("d.w", value="10")
 
-        self.expect("frame variable 'e.x'", error=True,
-                    substrs=["no member named 'x' in 'E'"])
-        self.expect("frame variable 'f.x'", error=True,
-                    substrs=["no member named 'x' in 'F'"])
+        self.expect(
+            "frame variable 'e.x'", error=True, substrs=["no member named 'x' in 'E'"]
+        )
+        self.expect(
+            "frame variable 'f.x'", error=True, substrs=["no member named 'x' in 'F'"]
+        )
         self.expect_var_path("f.named_field.x", value="12")
 
         self.expect_var_path("unnamed_derived.y", value="2")
         self.expect_var_path("unnamed_derived.z", value="13")
 
-        self.expect("frame variable 'derb.x'", error=True,
-                    substrs=["no member named 'x' in 'DerivedB'"])
-        self.expect("frame variable 'derb.y'", error=True,
-                    substrs=["no member named 'y' in 'DerivedB'"])
+        self.expect(
+            "frame variable 'derb.x'",
+            error=True,
+            substrs=["no member named 'x' in 'DerivedB'"],
+        )
+        self.expect(
+            "frame variable 'derb.y'",
+            error=True,
+            substrs=["no member named 'y' in 'DerivedB'"],
+        )
         self.expect_var_path("derb.w", value="14")
         self.expect_var_path("derb.k", value="15")
         self.expect_var_path("derb.a.x", value="1")
         self.expect_var_path("derb.a.y", value="2")
--- MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py	2025-05-01 07:05:57.000000 +0000
+++ MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py	2025-05-01 07:10:01.955423 +0000
@@ -9,23 +9,24 @@
 
 import os
 import shutil
 import time
 
+
 class TestFrameVarDILMemberOfInheritance(TestBase):
     # If your test case doesn't stress debug info, then
     # set this to true.  That way it won't be run once for
     # each debug info format.
     NO_DEBUG_INFO_TESTCASE = True
 
     def test_frame_var(self):
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
-                                          lldb.SBFileSpec("main.cpp"))
+        lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
 
-        self.expect("settings set target.experimental.use-DIL true",
-                    substrs=[""])
+        self.expect("settings set target.experimental.use-DIL true", substrs=[""])
         self.expect_var_path("a.a_", value="1")
         self.expect_var_path("b.b_", value="2")
         self.expect_var_path("c.a_", value="1")
         self.expect_var_path("c.b_", value="2")
         self.expect_var_path("c.c_", value="3")

Copy link

github-actions bot commented May 1, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp,h -- lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp lldb/include/lldb/ValueObject/DILAST.h lldb/include/lldb/ValueObject/DILEval.h lldb/include/lldb/ValueObject/DILLexer.h lldb/include/lldb/ValueObject/DILParser.h lldb/source/ValueObject/DILAST.cpp lldb/source/ValueObject/DILEval.cpp lldb/source/ValueObject/DILLexer.cpp lldb/source/ValueObject/DILParser.cpp
View the diff from clang-format here.
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index a74a12bd8..16ba34fab 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -94,7 +94,7 @@ public:
   MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow,
                ConstString name)
       : ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
-        m_is_arrow(is_arrow), m_field_name(name) { }
+        m_is_arrow(is_arrow), m_field_name(name) {}
 
   llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
 
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 1f1ad7161..e5c17fecb 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -280,7 +280,8 @@ Interpreter::EvaluateMemberOf(lldb::ValueObjectSP value,
 
   lldb::DynamicValueType use_dynamic =
       (!is_dynamic) ? lldb::eNoDynamicValues : lldb::eDynamicDontRunTarget;
-  // Walk the path from the base value to the value that contains the requested field.
+  // Walk the path from the base value to the value that contains the requested
+  // field.
   for (uint32_t idx : path) {
     member_val_sp = member_val_sp->GetChildAtIndex(idx, /*can_create*/ true);
   }
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 9c5bc7177..c7008f589 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -127,10 +127,9 @@ ASTNodeUP DILParser::ParsePostfixExpression() {
     m_dil_lexer.Advance();
     Token member_token = CurToken();
     std::string member_id = ParseIdExpression();
-    lhs = std::make_unique<MemberOfNode>(member_token.GetLocation(),
-                                         std::move(lhs),
-                                         token.GetKind() == Token::arrow,
-                                         ConstString(member_id));
+    lhs = std::make_unique<MemberOfNode>(
+        member_token.GetLocation(), std::move(lhs),
+        token.GetKind() == Token::arrow, ConstString(member_id));
   }
   return lhs;
 }
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
index dace888be..755febcdc 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
@@ -1,15 +1,13 @@
-int
-main(int argc, char**argv)
-{
+int main(int argc, char **argv) {
   int x = 2;
   struct Sx {
     int x;
-    int& r;
+    int &r;
     char y;
   } s{1, x, 2};
 
-  Sx& sr = s;
-  Sx* sp = &s;
+  Sx &sr = s;
+  Sx *sp = &s;
 
   Sx sarr[2] = {{5, x, 2}, {1, x, 3}};
 
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp
index 6237523ac..c787f29f9 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp
@@ -1,5 +1,4 @@
-int main(int argc, char** argv)
-{
+int main(int argc, char **argv) {
   struct A {
     struct {
       int x = 1;
@@ -22,7 +21,7 @@ int main(int argc, char** argv)
       int x = 5;
     };
     class {
-     public:
+    public:
       int y = 6;
     };
   } c;
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp
index 4d29af1b7..27177f3f0 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp
@@ -1,5 +1,4 @@
-int main(int argc, char** argv)
-{
+int main(int argc, char **argv) {
   struct A {
     int a_;
   } a{1};
@@ -38,7 +37,7 @@ int main(int argc, char** argv)
   } bat;
   bat.weight_ = 10;
 
- // Empty bases example.
+  // Empty bases example.
   struct IPlugin {
     virtual ~IPlugin() {}
   };
@@ -74,14 +73,14 @@ int main(int argc, char** argv)
   struct Mixin {};
   struct Parent : private Mixin, public Base {
     int z;
-    virtual void Do(){};
+    virtual void Do() {};
   };
   Parent obj;
   obj.x = 1;
   obj.y = 2;
   obj.z = 3;
-  Base* parent_base = &obj;
-  Parent* parent = &obj;
+  Base *parent_base = &obj;
+  Parent *parent = &obj;
 
   return 0; // Set a breakpoint here
 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants