Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 28 additions & 24 deletions indexer/SymbolFormatter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -510,34 +510,38 @@ SymbolFormatter::getEnumSymbol(const clang::EnumDecl &enumDecl) {
std::string_view SymbolFormatter::getFunctionDisambiguator(
const clang::FunctionDecl &functionDecl, char buf[16]) {
const clang::FunctionDecl *definingDecl = &functionDecl;
// clang-format off
if (functionDecl.isTemplateInstantiation()) {
// Handle non-templated member functions
if (auto *memberFnDecl = functionDecl.getInstantiatedFromMemberFunction()) {
definingDecl = memberFnDecl;
} else if (auto *templateInfo = functionDecl.getTemplateSpecializationInfo()) {
// Consider code like:
// template <typename T> class C { template <typename U> void f() {} };
// void g() { C<int>().f<int>(); }
// ^ Emitting a reference
//
// The dance below gets to the original declaration in 3 steps:
// C<int>.f<int> (FunctionDecl) → C<int>.f<$U> (FunctionTemplateDecl)
// ↓
// C<$T>.f<$U> (FunctionDecl) ← C<$T>.f<$U> (FunctionTemplateDecl)
auto *instantiatedTemplateDecl = templateInfo->getTemplate();
// For some reason, we end up on this code path for overloaded
// literal operators. In that case, uninstantiatedTemplateDecl
// can be null.
if (auto *uninstantiatedTemplateDecl = instantiatedTemplateDecl->getInstantiatedFromMemberTemplate()) {
definingDecl = uninstantiatedTemplateDecl->getTemplatedDecl();
}
if (functionDecl.isTemplateInstantiation()) {
// Handle non-templated member functions of class templates
if (auto *memberFnDecl = functionDecl.getInstantiatedFromMemberFunction()) {
definingDecl = memberFnDecl;
} else if (auto *templateInfo =
functionDecl.getTemplateSpecializationInfo()) {
// Consider code like:
// template <typename T> class C { template <typename U> void f() {} };
// void g() { C<int>().f<int>(); }
// ^ Emitting a reference
//
// The dance below gets to the original declaration in 3 steps:
// C<int>.f<int> (FunctionDecl) → C<int>.f<$U> (FunctionTemplateDecl)
// ↓
// C<$T>.f<$U> (FunctionDecl) ← C<$T>.f<$U> (FunctionTemplateDecl)
auto *instantiatedTemplateDecl = templateInfo->getTemplate();
// For some reason, we end up on this code path for overloaded
// literal operators. In that case, uninstantiatedTemplateDecl
// can be null.
if (auto *uninstantiatedTemplateDecl =
instantiatedTemplateDecl->getInstantiatedFromMemberTemplate()) {
definingDecl = uninstantiatedTemplateDecl->getTemplatedDecl();
} else {
// For free function templates or member function templates that are
// not themselves inside a class template, use the template's decl
// directly to get the uninstantiated type signature.
definingDecl = instantiatedTemplateDecl->getTemplatedDecl();
}
}
// clang-format on
}
// 64-bit hash in hex should take 16 characters at most.
auto typeString = definingDecl->getType().getCanonicalType().getAsString();
// char buf[16] = {0};
auto *end = fmt::format_to(buf, "{:x}", HashValue::forText(typeString));
return std::string_view{buf, end};
}
Expand Down
2 changes: 1 addition & 1 deletion test/index/cuda/kernelcall.snapshot.cu
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@
}
void e() { d0(1); }
// ^ definition [..] b#e(49f6e7a06ebc5aa8).
// ^^ reference [..] b#d0(d4f767463ce0a6b3).
// ^^ reference [..] b#d0(9b289cee16747614).
};

namespace x {
Expand Down
84 changes: 84 additions & 0 deletions test/index/functions/enable_if_templates.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Test for member function templates with enable_if in non-template classes,
// and free function templates with enable_if. These patterns were previously
// broken because getFunctionDisambiguator didn't handle the case where
// getInstantiatedFromMemberTemplate() returns null for such templates.

namespace std {
template <bool B, class T = void>
struct enable_if {};

template <class T>
struct enable_if<true, T> {
using type = T;
};

template <class T>
struct is_integral {
static constexpr bool value = false;
};

template <>
struct is_integral<int> {
static constexpr bool value = true;
};

template <class T>
struct is_enum {
static constexpr bool value = false;
};
} // namespace std

// Issue 1: Member function template with enable_if in a non-template class
class Widget {
public:
template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void process(T value) {
(void)value;
}
};

// Issue 2: Free function template with enable_if
template <typename T,
typename std::enable_if<!std::is_enum<T>::value, bool>::type = false>
T convert(int x) {
return static_cast<T>(x);
}

// Issue 3: Member call through a pointer wrapper (simplified unique_ptr)
template <typename T>
class Ptr {
T *p;

public:
Ptr(T *ptr) : p(ptr) {}
T *operator->() const { return p; }
};

class ThreadLoop {
public:
bool isHealthy() const { return true; }
};

class ThreadPool {
Ptr<ThreadLoop> mThread;

public:
ThreadPool() : mThread(new ThreadLoop()) {}

bool checkHealth() const {
return mThread->isHealthy();
}
};

void test() {
Widget w;
w.process(42);
w.process<int>(100);

auto val = convert<int>(5);
(void)val;

ThreadPool pool;
(void)pool.checkHealth();
}
160 changes: 160 additions & 0 deletions test/index/functions/enable_if_templates.snapshot.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Test for member function templates with enable_if in non-template classes,
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition [..] `<file>/enable_if_templates.cc`/
// and free function templates with enable_if. These patterns were previously
// broken because getFunctionDisambiguator didn't handle the case where
// getInstantiatedFromMemberTemplate() returns null for such templates.

namespace std {
// ^^^ definition [..] std/
template <bool B, class T = void>
// ^ definition local 0
// ^ definition local 1
struct enable_if {};
// ^^^^^^^^^ definition [..] std/enable_if#

template <class T>
// ^ definition local 2
struct enable_if<true, T> {
// ^^^^^^^^^ definition [..] std/enable_if#
// ^ reference local 2
using type = T;
// ^^^^ definition [..] std/enable_if#type#
// ^ reference local 2
};

template <class T>
// ^ definition local 3
struct is_integral {
// ^^^^^^^^^^^ definition [..] std/is_integral#
static constexpr bool value = false;
// ^^^^^ definition [..] std/is_integral#value.
};

template <>
struct is_integral<int> {
// ^^^^^^^^^^^ reference [..] std/is_integral#
// ^^^^^^^^^^^ definition [..] std/is_integral#
static constexpr bool value = true;
// ^^^^^ definition [..] std/is_integral#value.
};

template <class T>
// ^ definition local 4
struct is_enum {
// ^^^^^^^ definition [..] std/is_enum#
static constexpr bool value = false;
// ^^^^^ definition [..] std/is_enum#value.
};
} // namespace std

// Issue 1: Member function template with enable_if in a non-template class
class Widget {
// ^^^^^^ definition [..] Widget#
public:
template <typename T,
// ^ definition local 5
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
// ^^^ reference [..] std/
// ^^^^^^^^^ reference [..] std/enable_if#
void process(T value) {
// ^^^^^^^ definition [..] Widget#process(9b289cee16747614).
// ^ reference local 5
// ^^^^^ definition local 6
(void)value;
// ^^^^^ reference local 6
}
};

// Issue 2: Free function template with enable_if
template <typename T,
// ^ definition local 7
typename std::enable_if<!std::is_enum<T>::value, bool>::type = false>
// ^^^ reference [..] std/
// ^^^^^^^^^ reference [..] std/enable_if#
T convert(int x) {
//^ reference local 7
// ^^^^^^^ definition [..] convert(767fea59dce4185d).
// ^ definition local 8
return static_cast<T>(x);
// ^ reference local 7
// ^ reference local 8
}

// Issue 3: Member call through a pointer wrapper (simplified unique_ptr)
template <typename T>
// ^ definition local 9
class Ptr {
// ^^^ definition [..] Ptr#
T *p;
// ^ reference local 9
// ^ definition [..] Ptr#p.

public:
Ptr(T *ptr) : p(ptr) {}
// ^^^ definition [..] Ptr#`Ptr<T>`(ebd0a1552f8ce24f).
// ^ reference local 9
// ^^^ definition local 10
// ^ reference [..] Ptr#p.
// ^^^ reference local 10
T *operator->() const { return p; }
// ^ reference local 9
// ^^^^^^^^ definition [..] Ptr#`operator->`(5a2a78a048fb49a8).
// ^ reference [..] Ptr#p.
};

class ThreadLoop {
// ^^^^^^^^^^ definition [..] ThreadLoop#
public:
bool isHealthy() const { return true; }
// ^^^^^^^^^ definition [..] ThreadLoop#isHealthy(50ce9a9e25b4a850).
};

class ThreadPool {
// ^^^^^^^^^^ definition [..] ThreadPool#
Ptr<ThreadLoop> mThread;
// ^^^ reference [..] Ptr#
// ^^^^^^^^^^ reference [..] ThreadLoop#
// ^^^^^^^ definition [..] ThreadPool#mThread.

public:
ThreadPool() : mThread(new ThreadLoop()) {}
// ^^^^^^^^^^ definition [..] ThreadPool#ThreadPool(49f6e7a06ebc5aa8).
// ^^^^^^^ reference [..] ThreadPool#mThread.
// ^^^^^^^ reference [..] Ptr#Ptr(ebd0a1552f8ce24f).
// ^^^^^^^^^^ reference [..] ThreadLoop#

bool checkHealth() const {
// ^^^^^^^^^^^ definition [..] ThreadPool#checkHealth(50ce9a9e25b4a850).
return mThread->isHealthy();
// ^^^^^^^ reference [..] ThreadPool#mThread.
// ^^ reference [..] Ptr#`operator->`(5a2a78a048fb49a8).
// ^^^^^^^^^ reference [..] ThreadLoop#isHealthy(50ce9a9e25b4a850).
}
};

void test() {
// ^^^^ definition [..] test(49f6e7a06ebc5aa8).
Widget w;
// ^^^^^^ reference [..] Widget#
// ^ definition local 11
w.process(42);
// ^ reference local 11
// ^^^^^^^ reference [..] Widget#process(9b289cee16747614).
w.process<int>(100);
// ^ reference local 11
// ^^^^^^^ reference [..] Widget#process(9b289cee16747614).

auto val = convert<int>(5);
// ^^^ definition local 12
// ^^^^^^^ reference [..] convert(767fea59dce4185d).
(void)val;
// ^^^ reference local 12

ThreadPool pool;
// ^^^^^^^^^^ reference [..] ThreadPool#
// ^^^^ definition local 13
// ^^^^ reference [..] ThreadPool#ThreadPool(49f6e7a06ebc5aa8).
(void)pool.checkHealth();
// ^^^^ reference local 13
// ^^^^^^^^^^^ reference [..] ThreadPool#checkHealth(50ce9a9e25b4a850).
}
2 changes: 1 addition & 1 deletion test/index/types/types.snapshot.cc
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@
};
return ignore_first("", L{});
// ^^^^^^^^^^^^ reference local 3
// ^ reference [..] trailing_return_type(693bfa61ed1914d5).$anonymous_type_4#`operator()`(dc97d1a1ce4cdab3).
// ^ reference [..] trailing_return_type(693bfa61ed1914d5).$anonymous_type_4#`operator()`(b691caff6c7f530).
// ^ reference [..] L#
}

Expand Down
Loading