[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.
This commit is contained in:
cmtice
2025-07-01 07:30:47 -07:00
committed by GitHub
parent ace5108f37
commit 11ecd4742b
5 changed files with 58 additions and 31 deletions

View File

@@ -562,6 +562,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
return ValueObjectConstResult::Create(nullptr, std::move(error));
}
var_sp = (*valobj_or_error)->GetVariable();
return *valobj_or_error;
}

View File

@@ -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("<invalid type>"), base->GetName());
return llvm::make_error<DILDiagnosticError>(
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("<invalid type>"), base->GetName());
return llvm::make_error<DILDiagnosticError>(
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}

View File

@@ -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<IdentifierNode>(loc, identifier);
if (!identifier.empty())
return std::make_unique<IdentifierNode>(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) {

View File

@@ -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")

View File

@@ -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")