From 11ecd4742b74697b8331d0051e533302e4d17c85 Mon Sep 17 00:00:00 2001 From: cmtice Date: Tue, 1 Jul 2025 07:30:47 -0700 Subject: [PATCH] [LLDB] Update DIL to pass current 'frame var' tests. (#145055) As a preliminary to making DIL the default implementation for 'frame var', ran check-lldb forcing 'frame var' to always use DIL, and discovered a few failing tests. This fixes most of them. The only remaining failing test is TestDAP_evaluate.py, which now passes a test case that the test says should fail (still investigating this). Changes in this PR: - Sets correct VariableSP, as well as returning ValueObjectSP (needed for several watchpoint tests). - Updates error messages, when looking up members, to match what the rest of LLDB expects. Also update appropriate DIL tests to expect the updated error messages. - Updates DIL parser to look for and accept "(anonymous namespace)::" at the front of a variable name. --- lldb/source/Target/StackFrame.cpp | 1 + lldb/source/ValueObject/DILEval.cpp | 10 +++--- lldb/source/ValueObject/DILParser.cpp | 26 ++++++++------ .../MemberOf/TestFrameVarDILMemberOf.py | 17 ++++++--- .../TestFrameVarDILMemberOfAnonymousMember.py | 35 +++++++++++++------ 5 files changed, 58 insertions(+), 31 deletions(-) diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 52a54020bc46..d97a81495218 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -562,6 +562,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath( return ValueObjectConstResult::Create(nullptr, std::move(error)); } + var_sp = (*valobj_or_error)->GetVariable(); return *valobj_or_error; } diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 4293990208c7..3a304b7a070e 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -363,8 +363,8 @@ Interpreter::Visit(const MemberOfNode *node) { if (!m_use_synthetic || !field_obj) { std::string errMsg = llvm::formatv( - "no member named '{0}' in {1}", node->GetFieldName(), - base->GetCompilerType().GetFullyUnqualifiedType().TypeDescription()); + "\"{0}\" is not a member of \"({1}) {2}\"", node->GetFieldName(), + base->GetTypeName().AsCString(""), base->GetName()); return llvm::make_error( m_expr, errMsg, node->GetLocation(), node->GetFieldName().size()); } @@ -383,9 +383,9 @@ Interpreter::Visit(const MemberOfNode *node) { 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()); + std::string errMsg = llvm::formatv( + "\"{0}\" is not a member of \"({1}) {2}\"", node->GetFieldName(), + base->GetTypeName().AsCString(""), base->GetName()); return llvm::make_error( m_expr, errMsg, node->GetLocation(), node->GetFieldName().size()); } diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 5abbfeabcea3..eac41fab9076 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -183,12 +183,14 @@ ASTNodeUP DILParser::ParsePostfixExpression() { // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { - if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) { + if (CurToken().IsOneOf( + {Token::coloncolon, Token::identifier, Token::l_paren})) { // Save the source location for the diagnostics message. uint32_t loc = CurToken().GetLocation(); - auto identifier = ParseIdExpression(); + std::string identifier = ParseIdExpression(); - return std::make_unique(loc, identifier); + if (!identifier.empty()) + return std::make_unique(loc, identifier); } if (CurToken().Is(Token::l_paren)) { @@ -232,16 +234,15 @@ std::string DILParser::ParseNestedNameSpecifier() { m_dil_lexer.LookAhead(4).Is(Token::coloncolon)) { m_dil_lexer.Advance(4); - assert( - (CurToken().Is(Token::identifier) || CurToken().Is(Token::l_paren)) && - "Expected an identifier or anonymous namespace, but not found."); + Expect(Token::coloncolon); + m_dil_lexer.Advance(); + if (!CurToken().Is(Token::identifier) && !CurToken().Is(Token::l_paren)) { + BailOut("Expected an identifier or anonymous namespace, but not found.", + CurToken().GetLocation(), CurToken().GetSpelling().length()); + } // Continue parsing the nested_namespace_specifier. std::string identifier2 = ParseNestedNameSpecifier(); - if (identifier2.empty()) { - Expect(Token::identifier); - identifier2 = CurToken().GetSpelling(); - m_dil_lexer.Advance(); - } + return "(anonymous namespace)::" + identifier2; } @@ -301,6 +302,9 @@ std::string DILParser::ParseIdExpression() { nested_name_specifier, unqualified_id); } + if (!CurToken().Is(Token::identifier)) + return ""; + // No nested_name_specifier, but with global scope -- this is also a // qualified_id production. Follow the second production rule. if (global_scope) { 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 index bb16c1f82489..ca6754a556d8 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py +++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py @@ -31,12 +31,19 @@ class TestFrameVarDILMemberOf(TestBase): 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->foo'", + error=True, + substrs=['"foo" is not a member of "(Sx *) sp"'], + ) - 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 '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") 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 index 1bde4706da90..856b3a069eec 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py +++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py @@ -27,8 +27,11 @@ class TestFrameVarDILMemberOfAnonymousMember(TestBase): 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( + "frame variable 'b.x'", + error=True, + substrs=['"x" is not a member of "(B) 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") @@ -43,19 +46,31 @@ class TestFrameVarDILMemberOfAnonymousMember(TestBase): 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=['"x" is not a member of "(E) e"'], + ) + self.expect( + "frame variable 'f.x'", + error=True, + substrs=['"x" is not a member of "(F) 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=['"x" is not a member of "(DerivedB) derb"'], + ) + self.expect( + "frame variable 'derb.y'", + error=True, + substrs=['"y" is not a member of "(DerivedB) derb"'], + ) 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")