Update index for function templates.

This commit is contained in:
ykiko
2024-11-07 22:04:54 +08:00
parent de8c86bffc
commit e910316329
5 changed files with 127 additions and 42 deletions

View File

@@ -175,7 +175,7 @@ public:
if(success) {
memory::Symbol& symbol = symbols.emplace_back();
llvm::SmallString<128> USR;
clang::index::generateUSRForDecl(decl, USR);
clang::index::generateUSRForDecl(canonical, USR);
symbol.id = llvm::xxHash64(USR);
symbol.name = decl->getNameAsString();
}
@@ -183,6 +183,10 @@ public:
return SymbolProxy(offset, *this);
}
bool alreadyVisited(const clang::Decl* decl) {
return symbolCache.contains(decl);
}
void indexTU(memory::Index& result, Compiler& compiler);
~SymbolBuilder() {

View File

@@ -135,6 +135,13 @@ public:
}
VISIT_DECL(FunctionDecl) {
/// Because `TraverseFunctionTemplateDecl` will also traverse it's templated function. We
/// already handled the template function in `VisitFunctionTemplateDecl`. So we skip them
/// here.
if(decl->getDescribedFunctionTemplate()) {
return true;
}
/// `void foo();`
/// ^~~~ declaration/definition
if(auto location = builder.addLocation(decl->getLocation())) {
@@ -212,7 +219,7 @@ public:
VISIT_DECL(ClassTemplateDecl) {
/// `template <typename T> class Foo { };`
/// ^~~~ definition
/// ^~~~ declaration/definition
if(auto location = builder.addLocation(decl->getLocation())) {
auto symbol = builder.addSymbol(decl);
symbol.addOccurrence(location);
@@ -228,7 +235,7 @@ public:
///
/// For full specialization:
/// `template <> class Foo<int> { };`
/// ^~~~ definition
/// ^~~~ declaration/definition
///
/// For explicit instantiation:
/// `template class Foo<int>;`
@@ -239,7 +246,7 @@ public:
auto symbol = builder.addSymbol(decl);
symbol.addOccurrence(location);
if(decl->isExplicitSpecialization()) {
symbol.addDefinition(location);
symbol.addDeclarationOrDefinition(decl->isThisDeclarationADefinition(), location);
} else {
symbol.addReference(location);
}
@@ -255,6 +262,36 @@ public:
symbol.addOccurrence(location);
symbol.addDeclarationOrDefinition(decl->isThisDeclarationADefinition(), location);
}
/// Clang doesn't add explicit instantiation of function template to its lexical context.
/// So we need to handle it here. Note that full specialization will be added and handle by
/// `VisitFunctionDecl`.
///
/// We may visit a function template decl multiple times, because it has multiple
/// declarations or definition. But we only need to visit explicit instantiation once.
if(builder.alreadyVisited(decl)) {
return true;
}
for(auto spec: decl->specializations()) {
/// `template void foo<int>();`
/// ^~~~ reference
auto kind = spec->getTemplateSpecializationKind();
if(kind == clang::TSK_ExplicitInstantiationDeclaration ||
kind == clang::TSK_ExplicitInstantiationDefinition) {
/// WORKAROUND: Clang currently doesn't record the location of explicit
/// instantiation. Use the location of the point of instantiation instead.
if(auto location = builder.addLocation(spec->getPointOfInstantiation())) {
auto symbol = builder.addSymbol(decl);
symbol.addOccurrence(location);
symbol.addReference(location);
}
/// FIXME: currently we only consider render name here. Render template arguments
/// and nested name specifier in explicit instantiation.
}
}
return true;
}

View File

@@ -1,23 +0,0 @@
#include "Tester.h"
namespace clice {
namespace {
TEST(Index, Annotation) {
const char* code = R"cpp(
int @name = 1;
int main() {
na$(d1)me = 2;
}
)cpp";
IndexerTester tester(code);
tester.GotoDefinition("d1", "name");
}
} // namespace
} // namespace clice

View File

@@ -6,32 +6,80 @@ using namespace clice;
TEST(Index, ClassTemplate) {
const char* code = R"cpp(
template <typename T, typename U>
struct $(primary_decl)foo;
using type = $(forward_full)foo<int, int>;
template <typename T, typename U>
struct $(primary)foo {};
template <typename T>
struct $(partial)foo<T, T> {};
struct $(partial_spec_decl)foo<T, T>;
template <typename T>
struct $(partial_spec)foo<T, T> {};
template <>
struct $(full)foo<int, int> {};
struct $(full_spec_decl)foo<int, int>;
template <>
struct $(full_spec)foo<int, int> {};
template struct $(explicit_primary)foo<char, int>;
template struct $(explicit_partial)foo<char, char>;
$(full1)foo<int, int> a;
$(primary1)foo<int, char> b;
$(primary2)foo<char, int> c;
$(partial1)foo<char, char> d;
$(implicit_primary_1)foo<int, char> b;
$(implicit_primary_2)foo<char, int> c;
$(implicit_partial)foo<char, char> d;
$(implicit_full)foo<int, int> a;
)cpp";
IndexerTester tester(code);
tester.GotoDefinition("primary_decl", "primary");
tester.GotoDefinition("explicit_primary", "primary");
tester.GotoDefinition("implicit_primary_1", "primary");
tester.GotoDefinition("implicit_primary_2", "primary");
tester.GotoDefinition("partial_spec_decl", "partial_spec");
tester.GotoDefinition("explicit_partial", "partial_spec");
tester.GotoDefinition("implicit_partial", "partial_spec");
tester.GotoDefinition("forward_full", "full_spec");
tester.GotoDefinition("full_spec_decl", "full_spec");
tester.GotoDefinition("implicit_full", "full_spec");
/// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc.
/// add tests for find references ..., !test symbol count.
}
TEST(Index, FunctionTemplate) {
/// Function template doesn't have partial specialization.
const char* code = R"cpp(
template <typename T> void $(primary_decl)foo();
template <typename T> void $(primary)foo() {}
template <> void $(spec_decl)foo<int>();
template <> void $(spec)foo<int>() {}
template void $(explicit_primary)foo<char>();
int main() {
$(implicit_primary)foo<char>();
$(implicit_spec)foo<int>();
}
)cpp";
IndexerTester tester(code, true);
tester.GotoDefinition("primary_decl", "primary");
tester.GotoDefinition("explicit_primary", "primary");
tester.GotoDefinition("explicit_partial", "partial");
tester.GotoDefinition("full1", "full");
tester.GotoDefinition("primary1", "primary");
tester.GotoDefinition("primary2", "primary");
tester.GotoDefinition("partial1", "partial");
tester.GotoDefinition("implicit_primary", "primary");
tester.GotoDefinition("spec_decl", "spec");
tester.GotoDefinition("implicit_spec", "spec");
}
} // namespace

View File

@@ -69,12 +69,31 @@ struct IndexerTester {
mainFile = {static_cast<uint32_t>(files.begin() - loader->files().begin())};
}
IndexerTester& GotoDefinition(llvm::StringRef position, llvm::StringRef target) {
void GotoDefinition(llvm::StringRef position) {
auto loc = annotation.position(position);
auto sym = loader->locateSymbol(mainFile, loc);
loader->lookupRelation(sym, RelationKind::Definition, [&](const FullLocation& location) {
EXPECT_EQ(location.begin, annotation.position(target));
});
bool success = loader->lookupRelation(sym,
RelationKind::Definition,
[&](const FullLocation& location) {
llvm::outs() << json::serialize(location) << "\n";
});
EXPECT_TRUE(success);
}
IndexerTester& GotoDefinition(llvm::StringRef position,
llvm::StringRef target,
std::source_location info = std::source_location::current()) {
auto loc = annotation.position(position);
auto sym = loader->locateSymbol(mainFile, loc);
bool success =
loader->lookupRelation(sym,
RelationKind::Definition,
[&](const FullLocation& location) {
EXPECT_EQ(location.begin, annotation.position(target));
/// llvm::outs() << info.line() << ":" << info.column() <<
/// "\n";
});
EXPECT_TRUE(success);
return *this;
}
};