Update index for function templates.
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user