diff --git a/src/Index/SymbolBuilder.h b/src/Index/SymbolBuilder.h index 1ecd282a..87b5fae4 100644 --- a/src/Index/SymbolBuilder.h +++ b/src/Index/SymbolBuilder.h @@ -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() { diff --git a/src/Index/SymbolCollector.cpp b/src/Index/SymbolCollector.cpp index 09d4c2f2..0ca24560 100644 --- a/src/Index/SymbolCollector.cpp +++ b/src/Index/SymbolCollector.cpp @@ -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 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 { };` - /// ^~~~ definition + /// ^~~~ declaration/definition /// /// For explicit instantiation: /// `template class Foo;` @@ -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();` + /// ^~~~ 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; } diff --git a/unittests/Index/Index.cpp b/unittests/Index/Index.cpp deleted file mode 100644 index cb6f023a..00000000 --- a/unittests/Index/Index.cpp +++ /dev/null @@ -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 - diff --git a/unittests/Index/Template.cpp b/unittests/Index/Template.cpp index 18b2705a..df6997da 100644 --- a/unittests/Index/Template.cpp +++ b/unittests/Index/Template.cpp @@ -6,32 +6,80 @@ using namespace clice; TEST(Index, ClassTemplate) { const char* code = R"cpp( + template + struct $(primary_decl)foo; + + using type = $(forward_full)foo; + template struct $(primary)foo {}; template - struct $(partial)foo {}; + struct $(partial_spec_decl)foo; + + template + struct $(partial_spec)foo {}; template <> - struct $(full)foo {}; + struct $(full_spec_decl)foo; + + template <> + struct $(full_spec)foo {}; template struct $(explicit_primary)foo; template struct $(explicit_partial)foo; - $(full1)foo a; - $(primary1)foo b; - $(primary2)foo c; - $(partial1)foo d; + $(implicit_primary_1)foo b; + $(implicit_primary_2)foo c; + $(implicit_partial)foo d; + $(implicit_full)foo 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 void $(primary_decl)foo(); + + template void $(primary)foo() {} + + template <> void $(spec_decl)foo(); + + template <> void $(spec)foo() {} + + template void $(explicit_primary)foo(); + + int main() { + $(implicit_primary)foo(); + $(implicit_spec)foo(); + } )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 diff --git a/unittests/Index/Tester.h b/unittests/Index/Tester.h index a808eaec..a0659f77 100644 --- a/unittests/Index/Tester.h +++ b/unittests/Index/Tester.h @@ -69,12 +69,31 @@ struct IndexerTester { mainFile = {static_cast(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; } };