[Lldb-commits] [lldb] [LLDB] Add type casting to DIL, part 3 of 3 (PR #175061)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Feb 9 20:23:45 PST 2026
https://github.com/cmtice updated https://github.com/llvm/llvm-project/pull/175061
>From cf0161bf183944ab37c1e6952e5cd2e883b4d212 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 8 Jan 2026 11:45:30 -0800
Subject: [PATCH 1/5] [LLDB] Add type casting to DIL, part 3 of 3
This PR updates type parsing in DIL to recognize user-defined types
(classes, namespaces, etc.), and allows this to be used in type casting.
---
lldb/include/lldb/ValueObject/DILParser.h | 7 +
lldb/source/ValueObject/DILEval.cpp | 14 +-
lldb/source/ValueObject/DILParser.cpp | 176 +++++++++++++++++-
.../var-dil/expr/Casts/TestFrameVarDILCast.py | 26 +++
.../frame/var-dil/expr/Casts/main.cpp | 20 ++
5 files changed, 227 insertions(+), 16 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index bd2fc373cd9b5..3a6261aaac5fc 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -11,6 +11,7 @@
#include "lldb/Host/common/DiagnosticsRendering.h"
#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/StackFrame.h"
#include "lldb/Utility/Status.h"
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILLexer.h"
@@ -31,6 +32,9 @@ enum class ErrorCode : unsigned char {
kUnknown,
};
+llvm::Expected<lldb::TypeSystemSP>
+GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx);
+
// The following is modeled on class OptionParseError.
class DILDiagnosticError
: public llvm::ErrorInfo<DILDiagnosticError, DiagnosticError> {
@@ -103,6 +107,9 @@ class DILParser {
ASTNodeUP ParseCastExpression();
std::optional<CompilerType> ParseBuiltinType();
std::optional<CompilerType> ParseTypeId();
+ void ParseTypeSpecifierSeq(std::string &type_name);
+ bool ParseTypeSpecifier(std::string &user_type_name);
+ std::string ParseTypeName();
CompilerType ResolveTypeDeclarators(CompilerType type,
const std::vector<Token> &ptr_operators);
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 575dfae850c19..604c5da9d5aa7 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -13,6 +13,7 @@
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/DILParser.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/ValueObject/ValueObjectRegister.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
@@ -43,19 +44,6 @@ GetDynamicOrSyntheticValue(lldb::ValueObjectSP value_sp,
return value_sp;
}
-static llvm::Expected<lldb::TypeSystemSP>
-GetTypeSystemFromCU(std::shared_ptr<ExecutionContextScope> ctx) {
- auto stack_frame = ctx->CalculateStackFrame();
- if (!stack_frame)
- return llvm::createStringError("no stack frame in this context");
- SymbolContext symbol_context =
- stack_frame->GetSymbolContext(lldb::eSymbolContextCompUnit);
- lldb::LanguageType language = symbol_context.comp_unit->GetLanguage();
-
- symbol_context = stack_frame->GetSymbolContext(lldb::eSymbolContextModule);
- return symbol_context.module_sp->GetTypeSystemForLanguage(language);
-}
-
static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
lldb::BasicType basic_type) {
if (type_system)
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index f3027a3d82fa2..de09dd5f612bc 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -44,6 +44,72 @@ DILDiagnosticError::DILDiagnosticError(llvm::StringRef expr,
m_detail.rendered = std::move(rendered_msg);
}
+llvm::Expected<lldb::TypeSystemSP>
+GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) {
+ SymbolContext symbol_context =
+ ctx->GetSymbolContext(lldb::eSymbolContextCompUnit);
+ lldb::LanguageType language = symbol_context.comp_unit->GetLanguage();
+
+ symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule);
+ return symbol_context.module_sp->GetTypeSystemForLanguage(language);
+}
+
+CompilerType
+ResolveTypeByName(const std::string &name,
+ std::shared_ptr<ExecutionContextScope> ctx_scope) {
+ // Internally types don't have global scope qualifier in their names and
+ // LLDB doesn't support queries with it too.
+ llvm::StringRef name_ref(name);
+
+ if (name_ref.starts_with("::"))
+ name_ref = name_ref.drop_front(2);
+
+ std::vector<CompilerType> result_type_list;
+ lldb::TargetSP target_sp = ctx_scope->CalculateTarget();
+ const char *type_name = name_ref.data();
+ if (type_name && type_name[0] && target_sp) {
+ ModuleList &images = target_sp->GetImages();
+ ConstString const_type_name(type_name);
+ TypeQuery query(type_name);
+ TypeResults results;
+ images.FindTypes(nullptr, query, results);
+ for (const lldb::TypeSP &type_sp : results.GetTypeMap().Types())
+ if (type_sp)
+ result_type_list.push_back(type_sp->GetFullCompilerType());
+
+ if (auto process_sp = target_sp->GetProcessSP()) {
+ for (auto *runtime : process_sp->GetLanguageRuntimes()) {
+ if (auto *vendor = runtime->GetDeclVendor()) {
+ auto types = vendor->FindTypes(const_type_name, UINT32_MAX);
+ for (auto type : types)
+ result_type_list.push_back(type);
+ }
+ }
+ }
+ }
+
+ // We've found multiple types, try finding the "correct" one.
+ CompilerType full_match;
+ std::vector<CompilerType> partial_matches;
+
+ for (uint32_t i = 0; i < result_type_list.size(); ++i) {
+ CompilerType type = result_type_list[i];
+ llvm::StringRef type_name_ref = type.GetTypeName().GetStringRef();
+
+ if (type_name_ref == name_ref && type.IsValid())
+ return type;
+
+ if (type_name_ref.ends_with(name_ref))
+ partial_matches.push_back(type);
+ }
+
+ // If we have partial matches, pick a "random" one.
+ if (partial_matches.size() > 0)
+ return partial_matches.back();
+
+ return {};
+}
+
llvm::Expected<ASTNodeUP>
DILParser::Parse(llvm::StringRef dil_input_expr, DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
@@ -339,12 +405,32 @@ std::string DILParser::ParseNestedNameSpecifier() {
//
std::optional<CompilerType> DILParser::ParseTypeId() {
CompilerType type;
- // For now only allow builtin types -- will expand add to this later.
auto maybe_builtin_type = ParseBuiltinType();
if (maybe_builtin_type) {
type = *maybe_builtin_type;
- } else
- return {};
+ } else {
+ // Check to see if we have a user-defined type here.
+ // First build up the user-defined type name.
+ std::string type_name;
+ ParseTypeSpecifierSeq(type_name);
+
+ if (type_name.size() == 0)
+ return {};
+ type = ResolveTypeByName(type_name, m_ctx_scope);
+ if (!type.IsValid())
+ return {};
+
+ // Same-name identifiers should be preferred over typenames.
+ if (LookupIdentifier(type_name, m_ctx_scope, m_use_dynamic))
+ // TODO: Make type accessible with 'class', 'struct' and 'union' keywords.
+ return {};
+
+ // Same-name identifiers should be preferred over typenames.
+ if (LookupGlobalIdentifier(type_name, m_ctx_scope,
+ m_ctx_scope->CalculateTarget(), m_use_dynamic))
+ // TODO: Make type accessible with 'class', 'struct' and 'union' keywords
+ return {};
+ }
//
// abstract_declarator:
@@ -400,6 +486,90 @@ std::optional<CompilerType> DILParser::ParseBuiltinType() {
return {};
}
+// Parse a type_specifier_seq.
+//
+// type_specifier_seq:
+// type_specifier [type_specifier_seq]
+//
+void DILParser::ParseTypeSpecifierSeq(std::string &type_name) {
+ while (true) {
+ bool type_specifier = ParseTypeSpecifier(type_name);
+ if (!type_specifier) {
+ break;
+ }
+ }
+}
+
+// Parse a type_specifier.
+//
+// type_specifier:
+// ["::"] [nested_name_specifier] type_name
+//
+// Returns TRUE if a type_specifier was successfully parsed at this location.
+//
+bool DILParser::ParseTypeSpecifier(std::string &user_type_name) {
+ // The type_specifier must be a user-defined type. Try parsing a
+ // simple_type_specifier.
+ {
+ // Try parsing optional global scope operator.
+ bool global_scope = false;
+ if (CurToken().Is(Token::coloncolon)) {
+ global_scope = true;
+ m_dil_lexer.Advance();
+ }
+
+ // uint32_t loc = CurToken().GetLocation();
+
+ // Try parsing optional nested_name_specifier.
+ auto nested_name_specifier = ParseNestedNameSpecifier();
+
+ // Try parsing required type_name.
+ auto type_name = ParseTypeName();
+
+ // If there is a type_name, then this is indeed a simple_type_specifier.
+ // Global and qualified (namespace/class) scopes can be empty, since they're
+ // optional. In this case type_name is type we're looking for.
+ if (!type_name.empty()) {
+ // User-defined typenames can't be combined with builtin keywords.
+ user_type_name = llvm::formatv("{0}{1}{2}", global_scope ? "::" : "",
+ nested_name_specifier, type_name);
+ return true;
+ }
+ }
+
+ // No type_specifier was found here.
+ return false;
+}
+
+// Parse a type_name.
+//
+// type_name:
+// class_name
+// enum_name
+// typedef_name
+//
+// class_name
+// identifier
+//
+// enum_name
+// identifier
+//
+// typedef_name
+// identifier
+//
+std::string DILParser::ParseTypeName() {
+ // Typename always starts with an identifier.
+ if (CurToken().IsNot(Token::identifier)) {
+ return "";
+ }
+
+ // Otherwise look for a class_name, enum_name or a typedef_name.
+ std::string identifier = CurToken().GetSpelling();
+ m_dil_lexer.Advance();
+
+ return identifier;
+}
+
// Parse an id_expression.
//
// id_expression:
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
index b1f91e55353f3..c999b420f066e 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
@@ -117,10 +117,34 @@ def test_type_cast(self):
substrs=["cannot convert 'ns::Foo' to 'int'"],
)
+ # Test with typedefs and namespaces.
+ self.expect_var_path("(myint)1", type="myint", value="1")
+ self.expect_var_path("(myint)1LL", type="myint", value="1")
+ self.expect_var_path("(ns::myint)1", type="ns::myint", value="1")
+ self.expect_var_path("(::ns::myint)1", type="ns::myint", value="1")
+ self.expect_var_path("(::ns::myint)myint_", type="ns::myint", value="1")
+
self.expect_var_path("(int)myint_", type="int", value="1")
self.expect_var_path("(int)ns_myint_", type="int", value="2")
self.expect_var_path("(long long)myint_", type="long long", value="1")
self.expect_var_path("(long long)ns_myint_", type="long long", value="2")
+ self.expect_var_path("(::ns::myint)myint_", type="ns::myint", value="1")
+
+ self.expect_var_path(
+ "(ns::inner::mydouble)1", type="ns::inner::mydouble", value="1"
+ )
+ self.expect_var_path(
+ "(::ns::inner::mydouble)1.2", type="ns::inner::mydouble", value="1.2"
+ )
+ self.expect_var_path(
+ "(ns::inner::mydouble)myint_", type="ns::inner::mydouble", value="1"
+ )
+ self.expect_var_path(
+ "(::ns::inner::mydouble)ns_inner_mydouble_",
+ type="ns::inner::mydouble",
+ value="1.2",
+ )
+ self.expect_var_path("(myint)ns_inner_mydouble_", type="myint", value="1")
# Test with pointers and arrays.
self.expect_var_path("(long long)ap", type="long long")
@@ -180,6 +204,8 @@ def test_type_cast(self):
error=True,
substrs=["cannot cast from type 'double' to pointer type 'char *'"],
)
+ self.expect_var_path("(ns::Foo*)ns_inner_foo_ptr_", type="ns::Foo *")
+ self.expect_var_path("(ns::inner::Foo*)ns_foo_ptr_", type="ns::inner::Foo *")
self.expect_var_path("*(int*)(void*)ap", type="int", value="1")
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
index 3977283f54cc6..7c83f18b70a34 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
@@ -8,6 +8,14 @@ typedef int myint;
class Foo {};
+namespace inner {
+
+using mydouble = double;
+
+class Foo {};
+
+} // namespace inner
+
} // namespace ns
int main(int argc, char **argv) {
@@ -33,12 +41,24 @@ int main(int argc, char **argv) {
ns::Foo ns_foo_;
ns::Foo *ns_foo_ptr_ = &ns_foo_;
+ ns::inner::mydouble ns_inner_mydouble_ = 1.2;
+ ns::inner::Foo ns_inner_foo_;
+ ns::inner::Foo *ns_inner_foo_ptr_ = &ns_inner_foo_;
+
float finf = std::numeric_limits<float>::infinity();
float fnan = std::numeric_limits<float>::quiet_NaN();
float fsnan = std::numeric_limits<float>::signaling_NaN();
float fmax = std::numeric_limits<float>::max();
float fdenorm = std::numeric_limits<float>::denorm_min();
+ struct InnerFoo {
+ int a;
+ int b;
+ };
+
+ InnerFoo ifoo;
+ (void)ifoo;
+
int arr_1d[] = {1, 2, 3, 4};
int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}};
>From 0a7b7467d3a1f73ccfddac1afae08585bd97fa5b Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 15 Jan 2026 14:58:08 -0800
Subject: [PATCH 2/5] - Use std::optional<std::string> for return values in
ParseTypeName and ParseTypeSpecifier. - Remove output parameter from
ParseTypeSpecifier. - Other small reviewer requested edits.
---
lldb/include/lldb/ValueObject/DILParser.h | 4 +-
lldb/source/ValueObject/DILParser.cpp | 62 +++++++++++------------
2 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 3a6261aaac5fc..2defcf7bf3793 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -108,8 +108,8 @@ class DILParser {
std::optional<CompilerType> ParseBuiltinType();
std::optional<CompilerType> ParseTypeId();
void ParseTypeSpecifierSeq(std::string &type_name);
- bool ParseTypeSpecifier(std::string &user_type_name);
- std::string ParseTypeName();
+ std::optional<std::string> ParseTypeSpecifier();
+ std::optional<std::string> ParseTypeName();
CompilerType ResolveTypeDeclarators(CompilerType type,
const std::vector<Token> &ptr_operators);
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index de09dd5f612bc..51e25fd732456 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -414,7 +414,7 @@ std::optional<CompilerType> DILParser::ParseTypeId() {
std::string type_name;
ParseTypeSpecifierSeq(type_name);
- if (type_name.size() == 0)
+ if (type_name.empty())
return {};
type = ResolveTypeByName(type_name, m_ctx_scope);
if (!type.IsValid())
@@ -493,10 +493,10 @@ std::optional<CompilerType> DILParser::ParseBuiltinType() {
//
void DILParser::ParseTypeSpecifierSeq(std::string &type_name) {
while (true) {
- bool type_specifier = ParseTypeSpecifier(type_name);
- if (!type_specifier) {
+ std::optional<std::string> err_or_string = ParseTypeSpecifier();
+ if (!err_or_string)
break;
- }
+ type_name = *err_or_string;
}
}
@@ -507,38 +507,36 @@ void DILParser::ParseTypeSpecifierSeq(std::string &type_name) {
//
// Returns TRUE if a type_specifier was successfully parsed at this location.
//
-bool DILParser::ParseTypeSpecifier(std::string &user_type_name) {
+std::optional<std::string> DILParser::ParseTypeSpecifier() {
// The type_specifier must be a user-defined type. Try parsing a
// simple_type_specifier.
- {
- // Try parsing optional global scope operator.
- bool global_scope = false;
- if (CurToken().Is(Token::coloncolon)) {
- global_scope = true;
- m_dil_lexer.Advance();
- }
-
- // uint32_t loc = CurToken().GetLocation();
-
- // Try parsing optional nested_name_specifier.
- auto nested_name_specifier = ParseNestedNameSpecifier();
- // Try parsing required type_name.
- auto type_name = ParseTypeName();
-
- // If there is a type_name, then this is indeed a simple_type_specifier.
- // Global and qualified (namespace/class) scopes can be empty, since they're
- // optional. In this case type_name is type we're looking for.
- if (!type_name.empty()) {
- // User-defined typenames can't be combined with builtin keywords.
- user_type_name = llvm::formatv("{0}{1}{2}", global_scope ? "::" : "",
- nested_name_specifier, type_name);
- return true;
- }
+ // Try parsing optional global scope operator.
+ bool global_scope = false;
+ if (CurToken().Is(Token::coloncolon)) {
+ global_scope = true;
+ m_dil_lexer.Advance();
}
+ // Try parsing optional nested_name_specifier.
+ auto nested_name_specifier = ParseNestedNameSpecifier();
+
+ // Try parsing required type_name.
+ auto type_name_or_err = ParseTypeName();
+ if (!type_name_or_err)
+ return type_name_or_err;
+ std::string type_name = *type_name_or_err;
+
+ // If there is a type_name, then this is indeed a simple_type_specifier.
+ // Global and qualified (namespace/class) scopes can be empty, since they're
+ // optional. In this case type_name is type we're looking for.
+ if (!type_name.empty())
+ // User-defined typenames can't be combined with builtin keywords.
+ return llvm::formatv("{0}{1}{2}", global_scope ? "::" : "",
+ nested_name_specifier, type_name);
+
// No type_specifier was found here.
- return false;
+ return {};
}
// Parse a type_name.
@@ -557,10 +555,10 @@ bool DILParser::ParseTypeSpecifier(std::string &user_type_name) {
// typedef_name
// identifier
//
-std::string DILParser::ParseTypeName() {
+std::optional<std::string> DILParser::ParseTypeName() {
// Typename always starts with an identifier.
if (CurToken().IsNot(Token::identifier)) {
- return "";
+ return std::nullopt;
}
// Otherwise look for a class_name, enum_name or a typedef_name.
>From 4b5a62bbd2e87b59c898868af979b8097148abce Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 15 Jan 2026 15:15:42 -0800
Subject: [PATCH 3/5] Update type cast test to verify that if a user variable
and a user-defined type have the same name, DIL prefers to interpret the name
as the variable.
---
.../var-dil/expr/Casts/TestFrameVarDILCast.py | 16 ++++++++++++++++
.../commands/frame/var-dil/expr/Casts/main.cpp | 8 ++++++++
2 files changed, 24 insertions(+)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
index c999b420f066e..8f7948ec82406 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
@@ -259,3 +259,19 @@ def test_type_cast(self):
self.expect_var_path("((int*)arr_2d)[1]", type="int", value="2")
self.expect_var_path("((int*)arr_2d)[2]", type="int", value="3")
self.expect_var_path("((int*)arr_2d[1])[1]", type="int", value="5")
+
+ # Test casting to user-defined type with same name as variable.
+
+ self.expect_var_path("myStruct", type="myName")
+ self.expect_var_path("myName", type="int", value="37")
+
+ # Here 'myName' is treated as a variable, not a type, so '(myName)'
+ # is parsed as a variable expression and 'InnerFoo' is unexpected,
+ # and a type cast is not attempted.
+ self.expect(
+ "frame variable '(myName)InnerFoo'",
+ error=True,
+ substrs=[
+ "expected 'eof', got: <'InnerFoo' (identifier)>"
+ ],
+ )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
index 7c83f18b70a34..cdd4b8dfe72b2 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
@@ -62,5 +62,13 @@ int main(int argc, char **argv) {
int arr_1d[] = {1, 2, 3, 4};
int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}};
+ struct myName {
+ int x;
+ int y;
+ };
+
+ struct myName myStruct = { 98, 99 };
+ int myName = 37;
+
return 0; // Set a breakpoint here
}
>From e68b60229dea93f467374f573a4f389b8ffaffd0 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 15 Jan 2026 15:21:19 -0800
Subject: [PATCH 4/5] fix clang-format issues.
---
.../commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py | 4 +---
lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp | 2 +-
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
index 8f7948ec82406..90206d15f4bda 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
@@ -271,7 +271,5 @@ def test_type_cast(self):
self.expect(
"frame variable '(myName)InnerFoo'",
error=True,
- substrs=[
- "expected 'eof', got: <'InnerFoo' (identifier)>"
- ],
+ substrs=["expected 'eof', got: <'InnerFoo' (identifier)>"],
)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
index cdd4b8dfe72b2..5d4b88c557e73 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
@@ -67,7 +67,7 @@ int main(int argc, char **argv) {
int y;
};
- struct myName myStruct = { 98, 99 };
+ struct myName myStruct = {98, 99};
int myName = 37;
return 0; // Set a breakpoint here
>From e56ca107be703e6c223ae65a651e9b1c4f21e401 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 9 Feb 2026 20:18:55 -0800
Subject: [PATCH 5/5] Address reviewer comments: - Don't include StackFrame.h
in DILParser.h; forward declare class & put include statement in
DILParser.cpp. - Accept various suggested edits in ResolveTypeByName. -
Remove code from ResolveTypeByName for Obj-C (runtime vendor types). -
Remove use of partial matches from ResolveTypesByName. - Add test that
exercises LookupGlobalIdentifier path.
---
lldb/include/lldb/ValueObject/DILParser.h | 5 +++-
lldb/source/ValueObject/DILParser.cpp | 30 ++++---------------
.../var-dil/expr/Casts/TestFrameVarDILCast.py | 12 ++++++++
.../frame/var-dil/expr/Casts/main.cpp | 10 +++++++
4 files changed, 32 insertions(+), 25 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 2defcf7bf3793..f9b65dfe7d57e 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -11,7 +11,6 @@
#include "lldb/Host/common/DiagnosticsRendering.h"
#include "lldb/Target/ExecutionContextScope.h"
-#include "lldb/Target/StackFrame.h"
#include "lldb/Utility/Status.h"
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILLexer.h"
@@ -23,6 +22,10 @@
#include <tuple>
#include <vector>
+namespace lldb_private {
+ class StackFrame;
+}
+
namespace lldb_private::dil {
enum class ErrorCode : unsigned char {
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 51e25fd732456..63b3dfd01449e 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -16,6 +16,7 @@
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Target/ExecutionContextScope.h"
#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/StackFrame.h"
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILEval.h"
#include "llvm/ADT/StringRef.h"
@@ -56,7 +57,7 @@ GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) {
CompilerType
ResolveTypeByName(const std::string &name,
- std::shared_ptr<ExecutionContextScope> ctx_scope) {
+ ExecutionContextScope &ctx_scope) {
// Internally types don't have global scope qualifier in their names and
// LLDB doesn't support queries with it too.
llvm::StringRef name_ref(name);
@@ -65,27 +66,15 @@ ResolveTypeByName(const std::string &name,
name_ref = name_ref.drop_front(2);
std::vector<CompilerType> result_type_list;
- lldb::TargetSP target_sp = ctx_scope->CalculateTarget();
- const char *type_name = name_ref.data();
- if (type_name && type_name[0] && target_sp) {
+ lldb::TargetSP target_sp = ctx_scope.CalculateTarget();
+ if (!name_ref.empty() && target_sp) {
ModuleList &images = target_sp->GetImages();
- ConstString const_type_name(type_name);
- TypeQuery query(type_name);
+ TypeQuery query{ConstString(name_ref)};
TypeResults results;
images.FindTypes(nullptr, query, results);
for (const lldb::TypeSP &type_sp : results.GetTypeMap().Types())
if (type_sp)
result_type_list.push_back(type_sp->GetFullCompilerType());
-
- if (auto process_sp = target_sp->GetProcessSP()) {
- for (auto *runtime : process_sp->GetLanguageRuntimes()) {
- if (auto *vendor = runtime->GetDeclVendor()) {
- auto types = vendor->FindTypes(const_type_name, UINT32_MAX);
- for (auto type : types)
- result_type_list.push_back(type);
- }
- }
- }
}
// We've found multiple types, try finding the "correct" one.
@@ -98,15 +87,8 @@ ResolveTypeByName(const std::string &name,
if (type_name_ref == name_ref && type.IsValid())
return type;
-
- if (type_name_ref.ends_with(name_ref))
- partial_matches.push_back(type);
}
- // If we have partial matches, pick a "random" one.
- if (partial_matches.size() > 0)
- return partial_matches.back();
-
return {};
}
@@ -416,7 +398,7 @@ std::optional<CompilerType> DILParser::ParseTypeId() {
if (type_name.empty())
return {};
- type = ResolveTypeByName(type_name, m_ctx_scope);
+ type = ResolveTypeByName(type_name, *m_ctx_scope);
if (!type.IsValid())
return {};
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
index 90206d15f4bda..6e83622ef9fb9 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
@@ -265,6 +265,9 @@ def test_type_cast(self):
self.expect_var_path("myStruct", type="myName")
self.expect_var_path("myName", type="int", value="37")
+ self.expect_var_path("secondStruct", type="myGlobalName")
+ self.expect_var_path("myGlobalName", type="bool", value="true")
+
# Here 'myName' is treated as a variable, not a type, so '(myName)'
# is parsed as a variable expression and 'InnerFoo' is unexpected,
# and a type cast is not attempted.
@@ -273,3 +276,12 @@ def test_type_cast(self):
error=True,
substrs=["expected 'eof', got: <'InnerFoo' (identifier)>"],
)
+
+ # Here 'myGlobalName' is treated as a (global) variable, not a type,
+ # so '(myGlobalName)' is parsed as a variable expression and 'InnerFoo'
+ # is unexpected, and a type cast is not attempted.
+ self.expect(
+ "frame variable '(myGlobalName)InnerFoo'",
+ error=True,
+ substrs=["expected 'eof', got: <'InnerFoo' (identifier)>"],
+ )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
index 5d4b88c557e73..ee427185ed00f 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/main.cpp
@@ -18,6 +18,9 @@ class Foo {};
} // namespace ns
+// Global variable
+bool myGlobalName = true;
+
int main(int argc, char **argv) {
int a = 1;
int *ap = &a;
@@ -70,5 +73,12 @@ int main(int argc, char **argv) {
struct myName myStruct = {98, 99};
int myName = 37;
+ struct myGlobalName {
+ int m;
+ bool bval;
+ };
+
+ struct myGlobalName secondStruct= {42, false};
+
return 0; // Set a breakpoint here
}
More information about the lldb-commits
mailing list