llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: None (cmtice)

<details>
<summary>Changes</summary>

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
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]

``````````

</details>


https://github.com/llvm/llvm-project/pull/138093
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to