Skip to content

Commit 4e750c3

Browse files
fix: Use unsubstituted types for disambiguators (#287)
This enables correct code navigation for member functions in templated classes. (The member functions may themselves be templated or non-templated.)
1 parent ade6cda commit 4e750c3

File tree

5 files changed

+169
-65
lines changed

5 files changed

+169
-65
lines changed

indexer/SymbolFormatter.cc

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -405,18 +405,45 @@ SymbolFormatter::getFunctionSymbol(const clang::FunctionDecl &functionDecl) {
405405
if (!optContextSymbol.has_value()) {
406406
return {};
407407
}
408-
// See discussion in docs/Design.md for this choice of disambiguator.
408+
const clang::FunctionDecl *definingDecl = &functionDecl;
409+
// clang-format off
410+
if (functionDecl.isTemplateInstantiation()) {
411+
// Handle non-templated member functions
412+
if (auto *memberFnDecl = functionDecl.getInstantiatedFromMemberFunction()) {
413+
definingDecl = memberFnDecl;
414+
} else if (auto *templateInfo = functionDecl.getTemplateSpecializationInfo()) {
415+
// Consider code like:
416+
// template <typename T> class C { template <typename U> void f() {} };
417+
// void g() { C<int>().f<int>(); }
418+
// ^ Emitting a reference
419+
//
420+
// The dance below gets to the original declaration in 3 steps:
421+
// C<int>.f<int> (FunctionDecl) → C<int>.f<$U> (FunctionTemplateDecl)
422+
//
423+
// C<$T>.f<$U> (FunctionDecl) ← C<$T>.f<$U> (FunctionTemplateDecl)
424+
auto *instantiatedTemplateDecl = templateInfo->getTemplate();
425+
// For some reason, we end up on this code path for overloaded
426+
// literal operators. In that case, uninstantiatedTemplateDecl
427+
// can be null.
428+
if (auto *uninstantiatedTemplateDecl = instantiatedTemplateDecl->getInstantiatedFromMemberTemplate()) {
429+
definingDecl = uninstantiatedTemplateDecl->getTemplatedDecl();
430+
}
431+
}
432+
}
433+
// clang-format on
434+
auto name = this->formatTemporary(functionDecl);
435+
// 64-bit hash in hex should take 16 characters at most.
409436
auto typeString =
410-
functionDecl.getType().getCanonicalType().getAsString();
411-
auto name = functionDecl.getNameAsString();
437+
definingDecl->getType().getCanonicalType().getAsString();
438+
char buf[16] = {0};
439+
auto *end = fmt::format_to(buf, "{:x}", HashValue::forText(typeString));
440+
std::string_view disambiguator{buf, end};
412441
return SymbolBuilder::formatContextual(
413-
optContextSymbol.value(),
414-
DescriptorBuilder{
415-
.name = name,
416-
.disambiguator = this->formatTemporary(
417-
"{:x}", HashValue::forText(typeString)),
418-
.suffix = scip::Descriptor::Method,
419-
});
442+
optContextSymbol.value(), DescriptorBuilder{
443+
.name = name,
444+
.disambiguator = disambiguator,
445+
.suffix = scip::Descriptor::Method,
446+
});
420447
});
421448
}
422449

test/index/functions/methods.cc

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,22 +75,3 @@ void test_member_pointer() {
7575
M0 m{};
7676
(m.*p)();
7777
}
78-
79-
template <typename T>
80-
struct T0 {
81-
void f0() {}
82-
};
83-
84-
template <typename T>
85-
struct T1: T0<T> {
86-
void f1() {
87-
this->f0();
88-
}
89-
};
90-
91-
void test_template() {
92-
T0<int>().f0();
93-
T1<int>().f1();
94-
auto t1 = T1<int>();
95-
t1.f0();
96-
}

test/index/functions/methods.snapshot.cc

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -165,39 +165,3 @@
165165
// ^ reference local 1
166166
// ^ reference local 0
167167
}
168-
169-
template <typename T>
170-
// ^ definition local 2
171-
struct T0 {
172-
// ^^ definition [..] T0#
173-
void f0() {}
174-
// ^^ definition [..] T0#f0(49f6e7a06ebc5aa8).
175-
};
176-
177-
template <typename T>
178-
// ^ definition local 3
179-
struct T1: T0<T> {
180-
// ^^ definition [..] T1#
181-
// ^^ reference [..] T0#
182-
// ^ reference local 3
183-
void f1() {
184-
// ^^ definition [..] T1#f1(49f6e7a06ebc5aa8).
185-
this->f0();
186-
}
187-
};
188-
189-
void test_template() {
190-
// ^^^^^^^^^^^^^ definition [..] test_template(49f6e7a06ebc5aa8).
191-
T0<int>().f0();
192-
// ^^ reference [..] T0#
193-
// ^^ reference [..] T0#f0(49f6e7a06ebc5aa8).
194-
T1<int>().f1();
195-
// ^^ reference [..] T1#
196-
// ^^ reference [..] T1#f1(49f6e7a06ebc5aa8).
197-
auto t1 = T1<int>();
198-
// ^^ definition local 4
199-
// ^^ reference [..] T1#
200-
t1.f0();
201-
// ^^ reference local 4
202-
// ^^ reference [..] T0#f0(49f6e7a06ebc5aa8).
203-
}

test/index/functions/templates.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
template <typename T>
2+
struct T0 {
3+
void f0(T) {}
4+
5+
template <typename U>
6+
void g0(U) {}
7+
};
8+
9+
template <typename T>
10+
struct T1: T0<T> {
11+
void f1(T t) {
12+
this->f0(t);
13+
}
14+
15+
template <typename U>
16+
void g1(U u) {
17+
this->template g0<U>(u);
18+
}
19+
};
20+
21+
template <typename H>
22+
void h0(H) {}
23+
24+
template <typename H>
25+
void h1(H h) { h0<H>(h); }
26+
27+
void test_template() {
28+
T0<int>().f0(0);
29+
T1<int>().f1(0);
30+
auto t1 = T1<int>();
31+
t1.f0(0);
32+
33+
T0<int>().g0<int>(0);
34+
T1<int>().g1<unsigned>(0);
35+
auto t1_ = T1<int>();
36+
t1_.g0<char>(0);
37+
38+
h0<int>(0);
39+
h0<void *>(0);
40+
h1<int>(0);
41+
h1<char>(0);
42+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
template <typename T>
2+
//^^^^^^^^ definition [..] `<file>/templates.cc`/
3+
// ^ definition local 0
4+
struct T0 {
5+
// ^^ definition [..] T0#
6+
void f0(T) {}
7+
// ^^ definition [..] T0#f0(9b289cee16747614).
8+
// ^ reference local 0
9+
10+
template <typename U>
11+
// ^ definition local 1
12+
void g0(U) {}
13+
// ^^ definition [..] T0#g0(b07662a27bd562f9).
14+
// ^ reference local 1
15+
};
16+
17+
template <typename T>
18+
// ^ definition local 2
19+
struct T1: T0<T> {
20+
// ^^ definition [..] T1#
21+
// ^^ reference [..] T0#
22+
// ^ reference local 2
23+
void f1(T t) {
24+
// ^^ definition [..] T1#f1(9b289cee16747614).
25+
// ^ reference local 2
26+
// ^ definition local 3
27+
this->f0(t);
28+
// ^ reference local 3
29+
}
30+
31+
template <typename U>
32+
// ^ definition local 4
33+
void g1(U u) {
34+
// ^^ definition [..] T1#g1(b07662a27bd562f9).
35+
// ^ reference local 4
36+
// ^ definition local 5
37+
this->template g0<U>(u);
38+
// ^ reference local 4
39+
// ^ reference local 5
40+
}
41+
};
42+
43+
template <typename H>
44+
// ^ definition local 6
45+
void h0(H) {}
46+
// ^^ definition [..] h0(9b289cee16747614).
47+
// ^ reference local 6
48+
49+
template <typename H>
50+
// ^ definition local 7
51+
void h1(H h) { h0<H>(h); }
52+
// ^^ definition [..] h1(9b289cee16747614).
53+
// ^ reference local 7
54+
// ^ definition local 8
55+
// ^ reference local 7
56+
// ^ reference local 8
57+
58+
void test_template() {
59+
// ^^^^^^^^^^^^^ definition [..] test_template(49f6e7a06ebc5aa8).
60+
T0<int>().f0(0);
61+
// ^^ reference [..] T0#
62+
// ^^ reference [..] T0#f0(9b289cee16747614).
63+
T1<int>().f1(0);
64+
// ^^ reference [..] T1#
65+
// ^^ reference [..] T1#f1(9b289cee16747614).
66+
auto t1 = T1<int>();
67+
// ^^ definition local 9
68+
// ^^ reference [..] T1#
69+
t1.f0(0);
70+
// ^^ reference local 9
71+
// ^^ reference [..] T0#f0(9b289cee16747614).
72+
73+
T0<int>().g0<int>(0);
74+
// ^^ reference [..] T0#
75+
// ^^ reference [..] T0#g0(b07662a27bd562f9).
76+
T1<int>().g1<unsigned>(0);
77+
// ^^ reference [..] T1#
78+
// ^^ reference [..] T1#g1(b07662a27bd562f9).
79+
auto t1_ = T1<int>();
80+
// ^^^ definition local 10
81+
// ^^ reference [..] T1#
82+
t1_.g0<char>(0);
83+
// ^^^ reference local 10
84+
// ^^ reference [..] T0#g0(b07662a27bd562f9).
85+
86+
h0<int>(0);
87+
h0<void *>(0);
88+
h1<int>(0);
89+
h1<char>(0);
90+
}

0 commit comments

Comments
 (0)