Skip to content

Commit cf921e2

Browse files
fix: Improve approximate dependent method lookup (#321)
1 parent c39c12b commit cf921e2

File tree

6 files changed

+238
-13
lines changed

6 files changed

+238
-13
lines changed

indexer/ApproximateNameResolver.cc

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include "clang/AST/Decl.h"
2+
#include "clang/AST/DeclCXX.h"
3+
#include "clang/AST/DeclTemplate.h"
4+
#include "clang/AST/DeclarationName.h"
5+
#include "clang/AST/Type.h"
6+
#include "llvm/ADT/SmallVector.h"
7+
8+
#include "indexer/ApproximateNameResolver.h"
9+
#include "indexer/Enforce.h"
10+
11+
#include "spdlog/spdlog.h"
12+
13+
namespace scip_clang {
14+
15+
MemberLookupKey::MemberLookupKey(const clang::Type *type,
16+
const clang::DeclarationNameInfo &declNameInfo)
17+
: canonicalTypePtr(type->getCanonicalTypeInternal().getTypePtrOrNull()),
18+
declarationName(declNameInfo.getName().getAsOpaqueInteger()) {
19+
ENFORCE(this->canonicalTypePtr,
20+
"member lookups should exit early for null base type");
21+
}
22+
23+
llvm::SmallVector<const clang::NamedDecl *, 1>
24+
ApproximateNameResolver::tryResolveMember(
25+
const clang::Type *type, const clang::DeclarationNameInfo &declNameInfo) {
26+
llvm::SmallVector<const clang::NamedDecl *, 1> results;
27+
if (!type) {
28+
return results;
29+
}
30+
MemberLookupKey key{type, declNameInfo};
31+
auto it = this->dependentNameLookupCache.find(key);
32+
if (it != this->dependentNameLookupCache.end()) {
33+
return it->second;
34+
}
35+
36+
auto filter = [](const clang::NamedDecl *namedDecl) -> bool {
37+
return !llvm::isa<clang::UsingDecl>(namedDecl);
38+
};
39+
40+
llvm::SmallVector<const clang::Type *, 2> typesToLookup;
41+
typesToLookup.push_back(type);
42+
43+
while (!typesToLookup.empty()) {
44+
auto *type = typesToLookup.back();
45+
typesToLookup.pop_back();
46+
auto *cxxRecordDecl = Self::tryFindDeclForType(type);
47+
if (!cxxRecordDecl) {
48+
continue;
49+
}
50+
auto lookupResults =
51+
cxxRecordDecl->lookupDependentName(declNameInfo.getName(), filter);
52+
for (auto *namedDecl : lookupResults) {
53+
auto *unresolvedUsingValueDecl =
54+
llvm::dyn_cast<clang::UnresolvedUsingValueDecl>(namedDecl);
55+
if (!unresolvedUsingValueDecl) {
56+
results.push_back(namedDecl);
57+
continue;
58+
}
59+
if (auto *nestedNameSpecifier =
60+
unresolvedUsingValueDecl->getQualifier()) {
61+
if (auto *innerType = nestedNameSpecifier->getAsType()) {
62+
typesToLookup.push_back(innerType);
63+
}
64+
}
65+
}
66+
}
67+
68+
this->dependentNameLookupCache.insert({key, results});
69+
return results;
70+
}
71+
72+
// static
73+
clang::CXXRecordDecl *
74+
ApproximateNameResolver::tryFindDeclForType(const clang::Type *type) {
75+
if (const auto *recordType = type->getAs<clang::RecordType>())
76+
return llvm::dyn_cast<clang::CXXRecordDecl>(recordType->getDecl());
77+
78+
if (const auto *injectedClassNameType =
79+
type->getAs<clang::InjectedClassNameType>())
80+
type = injectedClassNameType->getInjectedSpecializationType()
81+
.getTypePtrOrNull();
82+
if (!type)
83+
return nullptr;
84+
85+
const auto *templateSpecializationType =
86+
type->getAs<clang::TemplateSpecializationType>();
87+
if (!templateSpecializationType)
88+
return nullptr;
89+
90+
auto *templateDecl = dyn_cast_or_null<clang::ClassTemplateDecl>(
91+
templateSpecializationType->getTemplateName().getAsTemplateDecl());
92+
if (!templateDecl)
93+
return nullptr;
94+
95+
return templateDecl->getTemplatedDecl();
96+
}
97+
98+
} // namespace scip_clang

indexer/ApproximateNameResolver.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#ifndef SCIP_CLANG_APPROX_NAME_RESOLVER
2+
#define SCIP_CLANG_APPROX_NAME_RESOLVER
3+
4+
#include <utility>
5+
6+
#include "absl/container/flat_hash_map.h"
7+
8+
#include "llvm/ADT/SmallVector.h"
9+
10+
#include "indexer/Derive.h"
11+
12+
namespace clang {
13+
class ASTContext;
14+
class CXXRecordDecl;
15+
class DeclarationNameInfo;
16+
class NamedDecl;
17+
class QualType;
18+
class Type;
19+
} // namespace clang
20+
21+
namespace scip_clang {
22+
23+
class MemberLookupKey {
24+
const clang::Type *canonicalTypePtr;
25+
uintptr_t declarationName;
26+
27+
public:
28+
MemberLookupKey(const clang::Type *, const clang::DeclarationNameInfo &);
29+
30+
template <typename H>
31+
friend H AbslHashValue(H h, const MemberLookupKey &self) {
32+
return H::combine(std::move(h), (void *)self.canonicalTypePtr,
33+
self.declarationName);
34+
}
35+
36+
DERIVE_CMP_ALL(MemberLookupKey)
37+
};
38+
39+
/// Type similar to clangd's HeuristicResolver, used for performing
40+
/// best-effort name resolution when encountered unresolved names.
41+
///
42+
/// We don't directly reuse the code from clangd, since it's not that
43+
/// much code (<300 lines), and having own code let's us evolve it
44+
/// independently and add different heuristics.
45+
///
46+
/// (Named differently to reduce risk of confusion.)
47+
class ApproximateNameResolver {
48+
[[maybe_unused]] const clang::ASTContext &astContext; // for debugging
49+
using Self = ApproximateNameResolver;
50+
using ResultVec = llvm::SmallVector<const clang::NamedDecl *, 1>;
51+
absl::flat_hash_map<MemberLookupKey, ResultVec> dependentNameLookupCache;
52+
53+
public:
54+
ApproximateNameResolver(const clang::ASTContext &astContext)
55+
: astContext(astContext), dependentNameLookupCache() {}
56+
57+
ResultVec tryResolveMember(const clang::Type *,
58+
const clang::DeclarationNameInfo &);
59+
60+
private:
61+
// Analog to HeuristicResolver.cc's resolveTypeToRecordDecl
62+
static clang::CXXRecordDecl *tryFindDeclForType(const clang::Type *);
63+
};
64+
65+
} // namespace scip_clang
66+
67+
#endif // SCIP_CLANG_APPROX_NAME_RESOLVER

indexer/Indexer.cc

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "scip/scip.pb.h"
2828

2929
#include "indexer/AbslExtras.h"
30+
#include "indexer/ApproximateNameResolver.h"
3031
#include "indexer/DebugHelpers.h"
3132
#include "indexer/Enforce.h"
3233
#include "indexer/Indexer.h"
@@ -306,7 +307,8 @@ TuIndexer::TuIndexer(const clang::SourceManager &sourceManager,
306307
SymbolFormatter &symbolFormatter,
307308
GetStableFileId getStableFileId)
308309
: sourceManager(sourceManager), langOptions(langOptions),
309-
astContext(astContext), symbolFormatter(symbolFormatter), documentMap(),
310+
astContext(astContext), symbolFormatter(symbolFormatter),
311+
approximateNameResolver(astContext), documentMap(),
310312
getStableFileId(getStableFileId), externalSymbols(),
311313
forwardDeclarations() {}
312314

@@ -891,21 +893,14 @@ void TuIndexer::trySaveMemberReferenceViaLookup(
891893
if (baseType.isNull()) {
892894
return;
893895
}
894-
clang::QualType actualBaseType = baseType.getCanonicalType();
896+
auto derefBaseType = baseType.getCanonicalType();
895897
// C++23's 'deducing this' feature allows the base type to be a reference
896898
if (baseType->isPointerType() || baseType->isReferenceType()) {
897-
actualBaseType = baseType->getPointeeType();
899+
derefBaseType = baseType->getPointeeType();
898900
}
899-
auto *recordDecl = actualBaseType->getAsCXXRecordDecl();
900-
if (!recordDecl) {
901-
return;
902-
}
903-
// Filter out UsingDecl as the corresponding UsingShadowDecl is sufficient.
904-
auto lookupResult = recordDecl->lookupDependentName(
905-
memberNameInfo.getName(), [&](const clang::NamedDecl *decl) -> bool {
906-
return !llvm::isa<clang::UsingDecl>(decl);
907-
});
908-
for (auto *namedDecl : lookupResult) {
901+
auto namedDecls = this->approximateNameResolver.tryResolveMember(
902+
derefBaseType.getTypePtrOrNull(), memberNameInfo);
903+
for (auto *namedDecl : namedDecls) {
909904
auto optSymbol = this->symbolFormatter.getNamedDeclSymbol(*namedDecl);
910905
if (optSymbol) {
911906
this->saveReference(*optSymbol, memberNameInfo.getLoc());

indexer/Indexer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "clang/Basic/SourceLocation.h"
1717
#include "llvm/ADT/SmallVector.h"
1818

19+
#include "indexer/ApproximateNameResolver.h"
1920
#include "indexer/ClangAstMacros.h"
2021
#include "indexer/Comparison.h"
2122
#include "indexer/Derive.h"
@@ -39,6 +40,7 @@ class DeclarationNameInfo;
3940
class LangOptions;
4041
class MacroDefinition;
4142
class MacroInfo;
43+
class NamedDecl;
4244
class NestedNameSpecifierLoc;
4345
class QualType;
4446
class SourceManager;
@@ -234,6 +236,8 @@ class TuIndexer final {
234236
const clang::LangOptions &langOptions;
235237
[[maybe_unused]] const clang::ASTContext &astContext;
236238
SymbolFormatter &symbolFormatter;
239+
ApproximateNameResolver approximateNameResolver;
240+
237241
absl::flat_hash_map<llvm_ext::AbslHashAdapter<clang::FileID>, PartialDocument>
238242
documentMap;
239243
GetStableFileId getStableFileId;

test/index/functions/templates.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,22 @@ void test_template() {
4040
h1<int>(0);
4141
h1<char>(0);
4242
}
43+
44+
template <typename T>
45+
struct Q0 {
46+
void f() {}
47+
};
48+
49+
template <typename T>
50+
struct Q1: Q0<T> {
51+
using Base1 = Q0<T>;
52+
using Base1::f;
53+
void g() { f(); }
54+
};
55+
56+
template <typename T>
57+
struct Q2: Q1<T> {
58+
using Base2 = Q1<T>;
59+
using Base2::f;
60+
void h() { f(); }
61+
};

test/index/functions/templates.snapshot.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,45 @@
9494
h1<char>(0);
9595
// ^^ reference [..] h1(9b289cee16747614).
9696
}
97+
98+
template <typename T>
99+
// ^ definition local 11
100+
struct Q0 {
101+
// ^^ definition [..] Q0#
102+
void f() {}
103+
// ^ definition [..] Q0#f(49f6e7a06ebc5aa8).
104+
};
105+
106+
template <typename T>
107+
// ^ definition local 12
108+
struct Q1: Q0<T> {
109+
// ^^ definition [..] Q1#
110+
// ^^ reference [..] Q0#
111+
// ^ reference local 12
112+
using Base1 = Q0<T>;
113+
// ^^^^^ definition [..] Q1#Base1#
114+
// ^^ reference [..] Q0#
115+
// ^ reference local 12
116+
using Base1::f;
117+
// ^^^^^ reference [..] Q1#Base1#
118+
void g() { f(); }
119+
// ^ definition [..] Q1#g(49f6e7a06ebc5aa8).
120+
// ^ reference [..] Q0#f(49f6e7a06ebc5aa8).
121+
};
122+
123+
template <typename T>
124+
// ^ definition local 13
125+
struct Q2: Q1<T> {
126+
// ^^ definition [..] Q2#
127+
// ^^ reference [..] Q1#
128+
// ^ reference local 13
129+
using Base2 = Q1<T>;
130+
// ^^^^^ definition [..] Q2#Base2#
131+
// ^^ reference [..] Q1#
132+
// ^ reference local 13
133+
using Base2::f;
134+
// ^^^^^ reference [..] Q2#Base2#
135+
void h() { f(); }
136+
// ^ definition [..] Q2#h(49f6e7a06ebc5aa8).
137+
// ^ reference [..] Q0#f(49f6e7a06ebc5aa8).
138+
};

0 commit comments

Comments
 (0)