Author: cmtice
Date: 2025-05-23T07:30:10-07:00
New Revision: 53d7b1d9e00d8cc3189808bbc26e2ee4577766e7

URL: 
https://github.com/llvm/llvm-project/commit/53d7b1d9e00d8cc3189808bbc26e2ee4577766e7
DIFF: 
https://github.com/llvm/llvm-project/commit/53d7b1d9e00d8cc3189808bbc26e2ee4577766e7.diff

LOG: [LLDB] Add field member operators to DIL (#138093)

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

Added: 
    lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile
    
lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
    lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
    lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile
    
lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py
    lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp
    lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile
    
lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py
    lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp

Modified: 
    lldb/docs/dil-expr-lang.ebnf
    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/Target/StackFrame.cpp
    lldb/source/ValueObject/DILAST.cpp
    lldb/source/ValueObject/DILEval.cpp
    lldb/source/ValueObject/DILLexer.cpp
    lldb/source/ValueObject/DILParser.cpp

Removed: 
    


################################################################################
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..8687316657ca9 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,
+               std::string name)
+      : ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
+        m_is_arrow(is_arrow), m_field_name(std::move(name)) {}
+
+  llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+  ASTNode *GetBase() const { return m_base.get(); }
+  bool GetIsArrow() const { return m_is_arrow; }
+  llvm::StringRef GetFieldName() const { return llvm::StringRef(m_field_name); 
}
+
+  static bool classof(const ASTNode *node) {
+    return node->GetKind() == NodeKind::eMemberOfNode;
+  }
+
+private:
+  ASTNodeUP m_base;
+  bool m_is_arrow;
+  std::string 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..266b6fb1a63eb 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -41,22 +41,27 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef 
name_ref,
 class Interpreter : Visitor {
 public:
   Interpreter(lldb::TargetSP target, llvm::StringRef expr,
-              lldb::DynamicValueType use_dynamic,
-              std::shared_ptr<StackFrame> frame_sp);
+              std::shared_ptr<StackFrame> frame_sp,
+              lldb::DynamicValueType use_dynamic, bool use_synthetic,
+              bool fragile_ivar, bool check_ptr_vs_member);
 
   llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node);
 
 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;
 
   // Used by the interpreter to create objects, perform casts, etc.
   lldb::TargetSP m_target;
   llvm::StringRef m_expr;
   lldb::ValueObjectSP m_scope;
-  lldb::DynamicValueType m_default_dynamic;
   std::shared_ptr<StackFrame> m_exe_ctx_scope;
+  lldb::DynamicValueType m_use_dynamic;
+  bool m_use_synthetic;
+  bool m_fragile_ivar;
+  bool m_check_ptr_vs_member;
 };
 
 } // namespace lldb_private::dil

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..bd152940d28f6 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();
@@ -117,6 +118,8 @@ class DILParser {
 
   lldb::DynamicValueType m_use_dynamic;
   bool m_use_synthetic;
+  bool m_fragile_ivar;
+  bool m_check_ptr_vs_member;
 }; // class DILParser
 
 } // namespace lldb_private::dil

diff  --git a/lldb/source/Target/StackFrame.cpp 
b/lldb/source/Target/StackFrame.cpp
index df9f1eae32bd1..ab5cd0b27c789 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -552,8 +552,9 @@ ValueObjectSP 
StackFrame::DILGetValueForVariableExpressionPath(
 
   // Evaluate the parsed expression.
   lldb::TargetSP target = this->CalculateTarget();
-  dil::Interpreter interpreter(target, var_expr, use_dynamic,
-                               shared_from_this());
+  dil::Interpreter interpreter(target, var_expr, shared_from_this(),
+                               use_dynamic, !no_synth_child, !no_fragile_ivar,
+                               check_ptr_vs_member);
 
   auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get());
   if (!valobj_or_error) {

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..07a41e8602369 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -201,10 +201,13 @@ lldb::ValueObjectSP LookupIdentifier(llvm::StringRef 
name_ref,
 }
 
 Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
-                         lldb::DynamicValueType use_dynamic,
-                         std::shared_ptr<StackFrame> frame_sp)
-    : m_target(std::move(target)), m_expr(expr), 
m_default_dynamic(use_dynamic),
-      m_exe_ctx_scope(frame_sp) {}
+                         std::shared_ptr<StackFrame> frame_sp,
+                         lldb::DynamicValueType use_dynamic, bool 
use_synthetic,
+                         bool fragile_ivar, bool check_ptr_vs_member)
+    : m_target(std::move(target)), m_expr(expr), m_exe_ctx_scope(frame_sp),
+      m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic),
+      m_fragile_ivar(fragile_ivar), m_check_ptr_vs_member(check_ptr_vs_member) 
{
+}
 
 llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) 
{
   // Evaluate an AST.
@@ -216,7 +219,7 @@ llvm::Expected<lldb::ValueObjectSP> 
Interpreter::Evaluate(const ASTNode *node) {
 
 llvm::Expected<lldb::ValueObjectSP>
 Interpreter::Visit(const IdentifierNode *node) {
-  lldb::DynamicValueType use_dynamic = m_default_dynamic;
+  lldb::DynamicValueType use_dynamic = m_use_dynamic;
 
   lldb::ValueObjectSP identifier =
       LookupIdentifier(node->GetName(), m_exe_ctx_scope, use_dynamic);
@@ -245,7 +248,7 @@ Interpreter::Visit(const UnaryOpNode *node) {
 
   switch (node->kind()) {
   case UnaryOpKind::Deref: {
-    lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
+    lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_use_dynamic);
     if (dynamic_rhs)
       rhs = dynamic_rhs;
 
@@ -272,4 +275,112 @@ Interpreter::Visit(const UnaryOpNode *node) {
       m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
 }
 
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const MemberOfNode *node) {
+  auto base_or_err = Evaluate(node->GetBase());
+  if (!base_or_err)
+    return base_or_err;
+  lldb::ValueObjectSP base = *base_or_err;
+
+  // Perform some basic type & correctness checking.
+  if (node->GetIsArrow()) {
+    if (!m_fragile_ivar) {
+      // Make sure we aren't trying to deref an objective
+      // C ivar if this is not allowed
+      const uint32_t pointer_type_flags =
+          base->GetCompilerType().GetTypeInfo(nullptr);
+      if ((pointer_type_flags & lldb::eTypeIsObjC) &&
+          (pointer_type_flags & lldb::eTypeIsPointer)) {
+        // This was an objective C object pointer and it was requested we
+        // skip any fragile ivars so return nothing here
+        return lldb::ValueObjectSP();
+      }
+    }
+
+    // If we have a non-pointer type with a synthetic value then lets check
+    // if we have a synthetic dereference specified.
+    if (!base->IsPointerType() && base->HasSyntheticValue()) {
+      Status deref_error;
+      if (lldb::ValueObjectSP synth_deref_sp =
+              base->GetSyntheticValue()->Dereference(deref_error);
+          synth_deref_sp && deref_error.Success()) {
+        base = std::move(synth_deref_sp);
+      }
+      if (!base || deref_error.Fail()) {
+        std::string errMsg = llvm::formatv(
+            "Failed to dereference synthetic value: {0}", deref_error);
+        return llvm::make_error<DILDiagnosticError>(
+            m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
+      }
+
+      // Some synthetic plug-ins fail to set the error in Dereference
+      if (!base) {
+        std::string errMsg = "Failed to dereference synthetic value";
+        return llvm::make_error<DILDiagnosticError>(
+            m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
+      }
+    }
+  }
+
+  if (m_check_ptr_vs_member) {
+    bool expr_is_ptr = node->GetIsArrow();
+    bool base_is_ptr = base->IsPointerType();
+
+    if (expr_is_ptr != base_is_ptr) {
+      if (base_is_ptr) {
+        std::string errMsg =
+            llvm::formatv("member reference type {0} is a pointer; "
+                          "did you mean to use '->'?",
+                          base->GetCompilerType().TypeDescription());
+        return llvm::make_error<DILDiagnosticError>(
+            m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
+      } else {
+        std::string errMsg =
+            llvm::formatv("member reference type {0} is not a pointer; "
+                          "did you mean to use '.'?",
+                          base->GetCompilerType().TypeDescription());
+        return llvm::make_error<DILDiagnosticError>(
+            m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
+      }
+    }
+  }
+
+  lldb::ValueObjectSP field_obj =
+      base->GetChildMemberWithName(node->GetFieldName());
+  if (!field_obj) {
+    if (m_use_synthetic) {
+      field_obj = base->GetSyntheticValue();
+      if (field_obj)
+        field_obj = field_obj->GetChildMemberWithName(node->GetFieldName());
+    }
+
+    if (!m_use_synthetic || !field_obj) {
+      std::string errMsg = llvm::formatv(
+          "no member named '{0}' in {1}", node->GetFieldName(),
+          base->GetCompilerType().GetFullyUnqualifiedType().TypeDescription());
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
+    }
+  }
+
+  if (field_obj && field_obj->GetName() == node->GetFieldName()) {
+    if (m_use_dynamic != lldb::eNoDynamicValues) {
+      lldb::ValueObjectSP dynamic_val_sp =
+          field_obj->GetDynamicValue(m_use_dynamic);
+      if (dynamic_val_sp)
+        field_obj = dynamic_val_sp;
+    }
+    return field_obj;
+  }
+
+  CompilerType base_type = base->GetCompilerType();
+  if (node->GetIsArrow() && base->IsPointerType())
+    base_type = base_type.GetPointeeType();
+  std::string errMsg =
+      llvm::formatv("no member named '{0}' in {1}", node->GetFieldName(),
+                    base_type.GetFullyUnqualifiedType().TypeDescription());
+  return llvm::make_error<DILDiagnosticError>(
+      m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
+}
+
 } // 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..b74fbeb0a7a0b 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -66,7 +66,8 @@ DILParser::DILParser(llvm::StringRef dil_input_expr, DILLexer 
lexer,
                      llvm::Error &error)
     : m_ctx_scope(frame_sp), m_input_expr(dil_input_expr),
       m_dil_lexer(std::move(lexer)), m_error(error), 
m_use_dynamic(use_dynamic),
-      m_use_synthetic(use_synthetic) {}
+      m_use_synthetic(use_synthetic), m_fragile_ivar(fragile_ivar),
+      m_check_ptr_vs_member(check_ptr_vs_member) {}
 
 ASTNodeUP DILParser::Run() {
   ASTNodeUP expr = ParseExpression();
@@ -79,15 +80,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 +112,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, 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..bb16c1f82489d
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
@@ -0,0 +1,43 @@
+"""
+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", type="int &")
+        self.expect_var_path("sr.x", value="1")
+        self.expect_var_path("sr.r", type="int &")
+        self.expect_var_path("sp->x", value="1")
+        self.expect_var_path("sp->r", type="int &")
+
+        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 '->'"])
+
+        # 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..80e8c064b1701
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
@@ -0,0 +1,18 @@
+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;
+
+  using SxAlias = Sx;
+  SxAlias sa{3, x, 4};
+
+  return 0; // Set a breakpoint here
+}

diff  --git 
a/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git 
a/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py
 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py
new file mode 100644
index 0000000000000..1bde4706da90f
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py
@@ -0,0 +1,62 @@
+"""
+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 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"))
+
+        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_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")
+
+        self.expect_var_path("c.x", value="5")
+        self.expect_var_path("c.y", value="6")
+
+        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_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_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")

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
new file mode 100644
index 0000000000000..6237523ac6bf3
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp
@@ -0,0 +1,74 @@
+int main(int argc, char** argv)
+{
+  struct A {
+    struct {
+      int x = 1;
+    };
+    int y = 2;
+  } a;
+
+  struct B {
+    // Anonymous struct inherits another struct.
+    struct : public A {
+      int z = 3;
+    };
+    int w = 4;
+    A a;
+  } b;
+
+  // Anonymous classes and unions.
+  struct C {
+    union {
+      int x = 5;
+    };
+    class {
+     public:
+      int y = 6;
+    };
+  } c;
+
+  // Multiple levels of anonymous structs.
+  struct D {
+    struct {
+      struct {
+        int x = 7;
+        struct {
+          int y = 8;
+        };
+      };
+      int z = 9;
+      struct {
+        int w = 10;
+      };
+    };
+  } d;
+
+  struct E {
+    struct IsNotAnon {
+      int x = 11;
+    };
+  } e;
+
+  struct F {
+    struct {
+      int x = 12;
+    } named_field;
+  } f;
+
+  // Inherited unnamed struct without an enclosing parent class.
+  struct : public A {
+    struct {
+      int z = 13;
+    };
+  } unnamed_derived;
+
+  struct DerivedB : public B {
+    struct {
+      // `w` in anonymous struct overrides `w` from `B`.
+      int w = 14;
+      int k = 15;
+    };
+  } derb;
+
+  return 0; // Set a breakpoint here
+}

diff  --git 
a/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git 
a/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py
 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py
new file mode 100644
index 0000000000000..c38ab5d30b238
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py
@@ -0,0 +1,49 @@
+"""
+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 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"))
+
+        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")
+        self.expect_var_path("d.a_", value="1")
+        self.expect_var_path("d.b_", value="2")
+        self.expect_var_path("d.c_", value="3")
+        self.expect_var_path("d.d_", value="4")
+        self.expect_var_path("d.fa_.a_", value="5")
+
+        self.expect_var_path("plugin.x", value="1")
+        self.expect_var_path("plugin.y", value="2")
+
+        self.expect_var_path("engine.x", value="1")
+        self.expect_var_path("engine.y", value="2")
+        self.expect_var_path("engine.z", value="3")
+
+        self.expect_var_path("parent_base->x", value="1")
+        self.expect_var_path("parent_base->y", value="2")
+        self.expect_var_path("parent->x", value="1")
+        self.expect_var_path("parent->y", value="2")
+        self.expect_var_path("parent->z", value="3")

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
new file mode 100644
index 0000000000000..4d29af1b7f15b
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp
@@ -0,0 +1,87 @@
+int main(int argc, char** argv)
+{
+  struct A {
+    int a_;
+  } a{1};
+
+  struct B {
+    int b_;
+  } b{2};
+
+  struct C : A, B {
+    int c_;
+  } c;
+  //  } c{{1}, {2}, 3};
+  c.a_ = 1;
+  c.b_ = 2;
+  c.c_ = 3;
+
+  struct D : C {
+    int d_;
+    A fa_;
+  } d;
+  //  } d{{{1}, {2}, 3}, 4, {5}};
+  d.a_ = 1;
+  d.b_ = 2;
+  d.c_ = 3;
+  d.d_ = 4;
+  d.fa_.a_ = 5;
+
+  // Virtual inheritance example.
+  struct Animal {
+    virtual ~Animal() = default;
+    int weight_;
+  };
+  struct Mammal : virtual Animal {};
+  struct WingedAnimal : virtual Animal {};
+  struct Bat : Mammal, WingedAnimal {
+  } bat;
+  bat.weight_ = 10;
+
+ // Empty bases example.
+  struct IPlugin {
+    virtual ~IPlugin() {}
+  };
+  struct Plugin : public IPlugin {
+    int x;
+    int y;
+  };
+  Plugin plugin;
+  plugin.x = 1;
+  plugin.y = 2;
+
+  struct ObjectBase {
+    int x;
+  };
+  struct Object : ObjectBase {};
+  struct Engine : Object {
+    int y;
+    int z;
+  };
+
+  Engine engine;
+  engine.x = 1;
+  engine.y = 2;
+  engine.z = 3;
+
+  // Empty multiple inheritance with empty base.
+  struct Base {
+    int x;
+    int y;
+    virtual void Do() = 0;
+    virtual ~Base() {}
+  };
+  struct Mixin {};
+  struct Parent : private Mixin, public Base {
+    int z;
+    virtual void Do(){};
+  };
+  Parent obj;
+  obj.x = 1;
+  obj.y = 2;
+  obj.z = 3;
+  Base* parent_base = &obj;
+  Parent* parent = &obj;
+
+  return 0; // Set a breakpoint here
+}


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to