Skip to content

Commit 15fff22

Browse files
authored
Merge pull request #8578 from Michael137/lldb/type-completion-rework/lazy-method-loading-to-next
[lldb][Type Completion] Load C++ methods lazily from DWARF
2 parents 09dabb3 + 41c4fca commit 15fff22

File tree

14 files changed

+225
-17
lines changed

14 files changed

+225
-17
lines changed

clang/include/clang/AST/ExternalASTSource.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
150150
virtual bool
151151
FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name);
152152

153+
virtual bool FindExternalVisibleMethodsByName(const DeclContext *DC,
154+
DeclarationName Name) {
155+
return false;
156+
}
157+
153158
/// Ensures that the table of all visible declarations inside this
154159
/// context is up to date.
155160
///

clang/lib/Sema/SemaExprMember.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,13 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
715715
}
716716
}
717717

718+
if (ExternalASTSource *Source =
719+
DC->getParentASTContext().getExternalSource()) {
720+
if (auto LookupName = R.getLookupName()) {
721+
Source->FindExternalVisibleMethodsByName(DC, LookupName);
722+
}
723+
}
724+
718725
// The record definition is complete, now look up the member.
719726
SemaRef.LookupQualifiedName(R, DC, SS);
720727

lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "ClangDeclVendor.h"
1212
#include "ClangModulesDeclVendor.h"
1313

14+
#include "NameSearchContext.h"
1415
#include "lldb/Core/Module.h"
1516
#include "lldb/Core/ModuleList.h"
1617
#include "lldb/Symbol/CompilerDeclContext.h"
@@ -21,6 +22,7 @@
2122
#include "lldb/Utility/LLDBLog.h"
2223
#include "lldb/Utility/Log.h"
2324
#include "clang/AST/ASTContext.h"
25+
#include "clang/AST/DeclCXX.h"
2426
#include "clang/Basic/SourceManager.h"
2527

2628
#include "Plugins/ExpressionParser/Clang/ClangUtil.h"
@@ -615,6 +617,125 @@ bool ClangASTSource::IgnoreName(const ConstString name,
615617
name_string_ref.starts_with("_$");
616618
}
617619

620+
bool ClangASTSource::FindExternalVisibleMethodsByName(
621+
const clang::DeclContext *DC, clang::DeclarationName Name) {
622+
if (!TypeSystemClang::UseRedeclCompletion())
623+
return true;
624+
625+
SmallVector<clang::NamedDecl *> decls;
626+
NameSearchContext context(*m_clang_ast_context, decls, Name, DC);
627+
FindExternalVisibleMethods(context);
628+
629+
return true;
630+
}
631+
632+
void ClangASTSource::FindExternalVisibleMethods(NameSearchContext &context) {
633+
assert(m_ast_context);
634+
635+
const ConstString name(context.m_decl_name.getAsString().c_str());
636+
CompilerDeclContext namespace_decl;
637+
FindExternalVisibleMethods(context, lldb::ModuleSP(), namespace_decl);
638+
}
639+
640+
bool ClangASTSource::CompilerDeclContextsMatch(
641+
CompilerDeclContext candidate_decl_ctx, DeclContext const *context,
642+
TypeSystemClang &ts) {
643+
auto CDC1 = candidate_decl_ctx.GetTypeSystem()->DeclContextGetCompilerContext(
644+
candidate_decl_ctx.GetOpaqueDeclContext());
645+
646+
// Member functions have at least 2 entries (1
647+
// for method name, 1 for parent class)
648+
assert(CDC1.size() > 1);
649+
650+
// drop last entry (which is function name)
651+
CDC1.pop_back();
652+
653+
const auto CDC2 = ts.DeclContextGetCompilerContext(
654+
const_cast<clang::DeclContext *>(context));
655+
656+
// Quick by-name check of the entire context hierarchy.
657+
if (CDC1 == CDC2)
658+
return true;
659+
660+
// Otherwise, check whether the 'candidate_decl_ctx' is a base class of
661+
// 'context'.
662+
auto const *candidate_context =
663+
(static_cast<clang::DeclContext *>(
664+
candidate_decl_ctx.GetOpaqueDeclContext()))
665+
->getParent();
666+
667+
auto const *candidate_cxx_record =
668+
dyn_cast<clang::CXXRecordDecl>(candidate_context);
669+
if (!candidate_cxx_record)
670+
return false;
671+
672+
auto const *cxx_record = dyn_cast<clang::CXXRecordDecl>(context);
673+
if (!cxx_record)
674+
return false;
675+
676+
// cxx_record comes from user expression AST. So we need to get origin
677+
// to compare against candidate_context.
678+
auto orig = GetDeclOrigin(cxx_record);
679+
if (!orig.Valid())
680+
return false;
681+
682+
if (llvm::cast<CXXRecordDecl>(orig.decl)->isDerivedFrom(candidate_cxx_record))
683+
return true;
684+
685+
return false;
686+
}
687+
688+
void ClangASTSource::FindExternalVisibleMethods(
689+
NameSearchContext &context, lldb::ModuleSP module_sp,
690+
CompilerDeclContext &namespace_decl) {
691+
assert(m_ast_context);
692+
693+
SymbolContextList sc_list;
694+
const ConstString name(context.m_decl_name.getAsString().c_str());
695+
if (!m_target)
696+
return;
697+
698+
if (context.m_found_type)
699+
return;
700+
701+
ModuleFunctionSearchOptions function_options;
702+
function_options.include_inlines = false;
703+
function_options.include_symbols = false;
704+
m_target->GetImages().FindFunctions(name, lldb::eFunctionNameTypeMethod,
705+
function_options, sc_list);
706+
707+
auto num_matches = sc_list.GetSize();
708+
if (num_matches == 0)
709+
return;
710+
711+
for (const SymbolContext &sym_ctx : sc_list) {
712+
assert(sym_ctx.function);
713+
CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext();
714+
if (!decl_ctx)
715+
continue;
716+
717+
assert(decl_ctx.IsClassMethod());
718+
719+
if (!CompilerDeclContextsMatch(decl_ctx, context.m_decl_context,
720+
context.m_clang_ts))
721+
continue;
722+
723+
clang::CXXMethodDecl *src_method = llvm::cast<CXXMethodDecl>(
724+
static_cast<clang::DeclContext *>(decl_ctx.GetOpaqueDeclContext()));
725+
Decl *copied_decl = CopyDecl(src_method);
726+
727+
if (!copied_decl)
728+
continue;
729+
730+
CXXMethodDecl *copied_method_decl = dyn_cast<CXXMethodDecl>(copied_decl);
731+
732+
if (!copied_method_decl)
733+
continue;
734+
735+
context.AddNamedDecl(copied_method_decl);
736+
}
737+
}
738+
618739
void ClangASTSource::FindExternalVisibleDecls(
619740
NameSearchContext &context, lldb::ModuleSP module_sp,
620741
CompilerDeclContext &namespace_decl) {

lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ class ClangASTSource : public ImporterBackedASTSource,
8787
bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
8888
clang::DeclarationName Name) override;
8989

90+
bool FindExternalVisibleMethodsByName(const clang::DeclContext *DC,
91+
clang::DeclarationName Name) override;
92+
9093
/// Enumerate all Decls in a given lexical context.
9194
///
9295
/// \param[in] DC
@@ -197,6 +200,7 @@ class ClangASTSource : public ImporterBackedASTSource,
197200
/// \param[in] context
198201
/// The NameSearchContext to use when filing results.
199202
virtual void FindExternalVisibleDecls(NameSearchContext &context);
203+
virtual void FindExternalVisibleMethods(NameSearchContext &context);
200204

201205
clang::Sema *getSema();
202206

@@ -219,6 +223,12 @@ class ClangASTSource : public ImporterBackedASTSource,
219223
return m_original.FindExternalVisibleDeclsByName(DC, Name);
220224
}
221225

226+
bool
227+
FindExternalVisibleMethodsByName(const clang::DeclContext *DC,
228+
clang::DeclarationName Name) override {
229+
return m_original.FindExternalVisibleMethodsByName(DC, Name);
230+
}
231+
222232
void FindExternalLexicalDecls(
223233
const clang::DeclContext *DC,
224234
llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
@@ -288,6 +298,9 @@ class ClangASTSource : public ImporterBackedASTSource,
288298
void FindExternalVisibleDecls(NameSearchContext &context,
289299
lldb::ModuleSP module,
290300
CompilerDeclContext &namespace_decl);
301+
void FindExternalVisibleMethods(NameSearchContext &context,
302+
lldb::ModuleSP module,
303+
CompilerDeclContext &namespace_decl);
291304

292305
/// Find all Objective-C methods matching a given selector.
293306
///
@@ -364,6 +377,10 @@ class ClangASTSource : public ImporterBackedASTSource,
364377
NameSearchContext &context,
365378
DeclFromUser<const clang::ObjCInterfaceDecl> &origin_iface_decl);
366379

380+
bool CompilerDeclContextsMatch(CompilerDeclContext candidate_decl_ctx,
381+
clang::DeclContext const *context,
382+
TypeSystemClang &ts);
383+
367384
protected:
368385
bool FindObjCMethodDeclsWithOrigin(
369386
NameSearchContext &context,

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,13 @@ void ClangExpressionDeclMap::LookupFunction(
13371337
}
13381338
}
13391339

1340+
void ClangExpressionDeclMap::FindExternalVisibleMethods(
1341+
NameSearchContext &context) {
1342+
assert(m_ast_context);
1343+
1344+
ClangASTSource::FindExternalVisibleMethods(context);
1345+
}
1346+
13401347
void ClangExpressionDeclMap::FindExternalVisibleDecls(
13411348
NameSearchContext &context, lldb::ModuleSP module_sp,
13421349
const CompilerDeclContext &namespace_decl) {

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "ClangASTSource.h"
1919
#include "ClangExpressionVariable.h"
2020

21+
#include "Plugins/ExpressionParser/Clang/NameSearchContext.h"
2122
#include "lldb/Core/Value.h"
2223
#include "lldb/Expression/Materializer.h"
2324
#include "lldb/Symbol/SymbolContext.h"
@@ -266,6 +267,7 @@ class ClangExpressionDeclMap : public ClangASTSource {
266267
/// \param[in] context
267268
/// The NameSearchContext that can construct Decls for this name.
268269
void FindExternalVisibleDecls(NameSearchContext &context) override;
270+
void FindExternalVisibleMethods(NameSearchContext &context) override;
269271

270272
/// Find all entities matching a given name in a given module/namespace,
271273
/// using a NameSearchContext to make Decls for them.

lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ class ClangExternalASTSourceCallbacks : public ImporterBackedASTSource {
3737
bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
3838
clang::DeclarationName Name) override;
3939

40+
bool FindExternalVisibleMethodsByName(const clang::DeclContext *DC,
41+
clang::DeclarationName Name) override {
42+
return false;
43+
}
44+
4045
void CompleteType(clang::TagDecl *tag_decl) override;
4146

4247
void CompleteType(clang::ObjCInterfaceDecl *objc_decl) override;

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,8 +2346,34 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
23462346
default_accessibility, layout_info);
23472347

23482348
// Now parse any methods if there were any...
2349-
for (const DWARFDIE &die : member_function_dies)
2350-
dwarf->ResolveType(die);
2349+
for (const DWARFDIE &mem : member_function_dies) {
2350+
if (!TypeSystemClang::UseRedeclCompletion() ||
2351+
type_is_objc_object_or_interface) {
2352+
dwarf->ResolveType(mem);
2353+
continue;
2354+
}
2355+
2356+
ConstString mem_name(mem.GetName());
2357+
ConstString die_name(die.GetName());
2358+
const bool is_ctor = mem_name == die_name;
2359+
const bool is_virtual_method =
2360+
mem.GetAttributeValueAsUnsigned(
2361+
DW_AT_virtuality, DW_VIRTUALITY_none) > DW_VIRTUALITY_none;
2362+
const bool is_operator = mem_name.GetStringRef().starts_with("operator");
2363+
const bool is_static_method =
2364+
mem.GetFirstChild().GetAttributeValueAsUnsigned(DW_AT_artificial,
2365+
0) == 0;
2366+
2367+
// FIXME: With RedeclCompletion, we currently don't have a good
2368+
// way to call `FindExternalVisibleMethods` from Clang
2369+
// for static functions, constructors or operators.
2370+
// So resolve them now.
2371+
//
2372+
// We want to resolve virtual methods now too because
2373+
// we set the method overrides below.
2374+
if (is_ctor || is_operator || is_virtual_method || is_static_method)
2375+
dwarf->ResolveType(mem);
2376+
}
23512377

23522378
if (type_is_objc_object_or_interface) {
23532379
ConstString class_name(clang_type.GetTypeName());

lldb/test/API/commands/expression/completion/TestExprCompletion.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
class CommandLineExprCompletionTestCase(TestBase):
1414
NO_DEBUG_INFO_TESTCASE = True
1515

16+
@expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true'))
1617
def test_expr_completion(self):
1718
self.build()
1819
self.main_source = "main.cpp"

lldb/test/API/commands/expression/context-object/TestContextObject.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,29 @@
55
import lldb
66
import lldbsuite.test.lldbutil as lldbutil
77
from lldbsuite.test.lldbtest import *
8-
8+
from lldbsuite.test.decorators import *
99

1010
class ContextObjectTestCase(TestBase):
11+
@expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true'))
12+
def test_context_object_eval_function(self):
13+
"""Tests expression evaluation of functions in context of an object."""
14+
self.build()
15+
16+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
17+
self, "// Break here", self.main_source_spec
18+
)
19+
frame = thread.GetFrameAtIndex(0)
20+
for obj in "cpp_struct", "cpp_struct_ref":
21+
obj_val = frame.FindVariable(obj)
22+
self.assertTrue(obj_val.IsValid())
23+
24+
# Test functions evaluation
25+
value = obj_val.EvaluateExpression("function()")
26+
self.assertTrue(value.IsValid())
27+
self.assertSuccess(value.GetError())
28+
self.assertEqual(value.GetValueAsSigned(), 2222)
29+
30+
1131
def test_context_object(self):
1232
"""Tests expression evaluation in context of an object."""
1333
self.build()
@@ -35,12 +55,6 @@ def test_context_object(self):
3555
self.assertSuccess(value.GetError())
3656
self.assertEqual(value.GetValueAsSigned(), 1111)
3757

38-
# Test functions evaluation
39-
value = obj_val.EvaluateExpression("function()")
40-
self.assertTrue(value.IsValid())
41-
self.assertSuccess(value.GetError())
42-
self.assertEqual(value.GetValueAsSigned(), 2222)
43-
4458
# Test that we retrieve the right global
4559
value = obj_val.EvaluateExpression("global.field")
4660
self.assertTrue(value.IsValid())

lldb/test/API/commands/expression/import-std-module/empty-module/TestEmptyStdModule.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class ImportStdModule(TestBase):
1515
@add_test_categories(["libc++"])
1616
@skipIfRemote
1717
@skipIf(compiler=no_match("clang"))
18+
@expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true'))
1819
def test(self):
1920
self.build()
2021

lldb/test/API/commands/expression/pr35310/TestExprsBug35310.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def setUp(self):
1212
self.main_source = "main.cpp"
1313
self.main_source_spec = lldb.SBFileSpec(self.main_source)
1414

15+
@expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true'))
1516
def test_issue35310(self):
1617
"""Test invoking functions with non-standard linkage names.
1718

lldb/test/API/lang/cpp/function-ref-qualifiers/TestCppFunctionRefQualifiers.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ def test(self):
3434
self.expect_expr("Foo{}.func()", result_type="int64_t", result_value="3")
3535

3636
self.filecheck("target modules dump ast", __file__)
37-
# CHECK: |-CXXMethodDecl {{.*}} func 'uint32_t () const &'
38-
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
39-
# CHECK-NEXT: |-CXXMethodDecl {{.*}} func 'int64_t () const &&'
40-
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
41-
# CHECK-NEXT: |-CXXMethodDecl {{.*}} func 'uint32_t () &'
42-
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
43-
# CHECK-NEXT: `-CXXMethodDecl {{.*}} func 'int64_t () &&'
44-
# CHECK-NEXT: `-AsmLabelAttr {{.*}}
37+
# CHECK-DAG: CXXMethodDecl {{.*}} func 'uint32_t () const &'
38+
# CHECK-DAG: `-AsmLabelAttr {{.*}}
39+
# CHECK-DAG: CXXMethodDecl {{.*}} func 'int64_t () const &&'
40+
# CHECK-DAG: `-AsmLabelAttr {{.*}}
41+
# CHECK-DAG: CXXMethodDecl {{.*}} func 'uint32_t () &'
42+
# CHECK-DAG: `-AsmLabelAttr {{.*}}
43+
# CHECK-DAG: CXXMethodDecl {{.*}} func 'int64_t () &&'
44+
# CHECK-DAG: `-AsmLabelAttr {{.*}}

lldb/test/API/python_api/class_members/TestSBTypeClassMembers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def setUp(self):
1919
self.source = "main.mm"
2020
self.line = line_number(self.source, "// set breakpoint here")
2121

22+
@expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true'))
2223
@skipUnlessDarwin
2324
def test(self):
2425
"""Test SBType APIs to fetch member function types."""

0 commit comments

Comments
 (0)