From 8e3615240661365c9f6650db08246ef6dd3886ae Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Sun, 5 Mar 2017 14:49:23 -0800 Subject: [PATCH] better template indexing --- command_line.cc | 2 +- indexer.cpp | 108 +++++-- libclangmm/Cursor.cc | 11 +- libclangmm/Cursor.h | 9 + test.cc | 12 +- tests/enums/enum_class_decl.cc | 2 + tests/enums/enum_decl.cc | 2 + tests/enums/enum_inherit.cc | 2 + tests/enums/enum_usage.cc | 2 + tests/foobar.cc | 281 ++++++++++++++++-- ...ass_template_func_usage_folded_into_one.cc | 51 ++++ ...ace_template_type_usage_folded_into_one.cc | 39 +++ ...mplate_class_func_usage_folded_into_one.cc | 48 +++ ...ass_template_func_usage_folded_into_one.cc | 49 +++ ...mplate_class_type_usage_folded_into_one.cc | 81 +++++ ...emplate_class_var_usage_folded_into_one.cc | 44 +++ .../template_func_usage_folded_into_one.cc | 40 +++ .../template_type_usage_folded_into_one.cc | 37 +++ .../template_var_usage_folded_into_one.cc | 73 +++++ 19 files changed, 841 insertions(+), 52 deletions(-) create mode 100644 tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc create mode 100644 tests/templates/namespace_template_type_usage_folded_into_one.cc create mode 100644 tests/templates/template_class_func_usage_folded_into_one.cc create mode 100644 tests/templates/template_class_template_func_usage_folded_into_one.cc create mode 100644 tests/templates/template_class_type_usage_folded_into_one.cc create mode 100644 tests/templates/template_class_var_usage_folded_into_one.cc create mode 100644 tests/templates/template_func_usage_folded_into_one.cc create mode 100644 tests/templates/template_type_usage_folded_into_one.cc create mode 100644 tests/templates/template_var_usage_folded_into_one.cc diff --git a/command_line.cc b/command_line.cc index 3d07f3d9..787ff256 100644 --- a/command_line.cc +++ b/command_line.cc @@ -604,7 +604,7 @@ void LanguageServerMain(std::string process_name) { -int main(int argc, char** argv) { +int mai232n(int argc, char** argv) { // We need to write to stdout in binary mode because in Windows, writing // \n will implicitly write \r\n. Language server API will ignore a // \r\r\n split request. diff --git a/indexer.cpp b/indexer.cpp index 10050765..be94673f 100644 --- a/indexer.cpp +++ b/indexer.cpp @@ -353,12 +353,9 @@ void VisitDeclForTypeUsageVisitorHandler(clang::Cursor cursor, VisitDeclForTypeU param->has_processed_any = true; IndexedFile* db = param->db; - // TODO: Something in STL (type_traits)? reports an empty USR. - std::string referenced_usr = cursor.get_referenced().get_usr(); - if (referenced_usr == "") - return; - + std::string referenced_usr = cursor.get_referenced().template_specialization_to_template_definition().get_usr(); TypeId ref_type_id = db->ToTypeId(referenced_usr); + if (!param->initial_type) param->initial_type = ref_type_id; @@ -392,7 +389,23 @@ clang::VisiterResult VisitDeclForTypeUsageVisitor(clang::Cursor cursor, clang::C return clang::VisiterResult::Continue; } -optional ResolveDeclToType(IndexedFile* db, clang::Cursor decl_cursor, +// Finds the cursor associated with the declaration type of |cursor|. This strips +// qualifies from |cursor| (ie, Foo* => Foo) and removes template arguments +// (ie, Foo => Foo<*,*>). +optional ResolveToDeclarationType(IndexedFile* db, clang::Cursor cursor) { + clang::Cursor declaration = cursor.get_type().strip_qualifiers().get_declaration(); + declaration = declaration.template_specialization_to_template_definition(); + std::string usr = declaration.get_usr(); + if (usr != "") + return db->ToTypeId(usr); + return nullopt; +} + +// Add usages to any seen TypeRef or TemplateRef under the given |decl_cursor|. This +// returns the first seen TypeRef or TemplateRef value, which can be useful if trying +// to figure out ie, what a using statement refers to. If trying to generally resolve +// a cursor to a type, use ResolveToDeclarationType, which works in more scenarios. +optional AddDeclUsages(IndexedFile* db, clang::Cursor decl_cursor, bool is_interesting, const CXIdxContainerInfo* semantic_container, const CXIdxContainerInfo* lexical_container) { // @@ -415,7 +428,46 @@ optional ResolveDeclToType(IndexedFile* db, clang::Cursor decl_cursor, // TypeRef struct S2 // TypeRef struct S2 // - + // + // Here is another example: + // + // enum A {}; + // enum B {}; + // + // template + // struct Foo { + // struct Inner {}; + // }; + // + // Foo::Inner a; + // Foo b; + // + // => + // + // EnumDecl A + // EnumDecl B + // ClassTemplate Foo + // TemplateTypeParameter T + // StructDecl Inner + // VarDecl a + // TemplateRef Foo + // TypeRef enum A + // TypeRef struct Foo::Inner + // CallExpr Inner + // VarDecl b + // TemplateRef Foo + // TypeRef enum B + // CallExpr Foo + // + // + // Determining the actual type of the variable/declaration from just the + // children is tricky. Doing so would require looking up the template + // definition associated with a TemplateRef, figuring out how many children + // it has, and then skipping that many TypeRef values. This also has to work + // with the example below (skipping the last TypeRef). As a result, we + // determine variable types using |ResolveToDeclarationType|. + // + // // We skip the last type reference for methods/variables which are defined // out-of-line w.r.t. the parent type. // @@ -449,7 +501,12 @@ optional ResolveDeclToType(IndexedFile* db, clang::Cursor decl_cursor, else { // If we are not processing the last type ref, it *must* be a TypeRef (ie, // and not a TemplateRef). - assert(!param.previous_cursor.has_value() || + // + // We will not visit every child if the is_interseting is false, so previous_cursor + // may not point to the last TemplateRef. + assert( + is_interesting == false || + param.previous_cursor.has_value() == false || param.previous_cursor.value().get_kind() == CXCursor_TypeRef); } @@ -496,12 +553,15 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { var_def->def.declaration = decl_loc; var_def->uses.push_back(decl_loc); + //std::cerr << std::endl << "Visiting declaration" << std::endl; + //Dump(decl_cursor); // Declaring variable type information. Note that we do not insert an // interesting reference for parameter declarations - that is handled when // the function declaration is encountered since we won't receive ParmDecl // declarations for unnamed parameters. - optional var_type = ResolveDeclToType(db, decl_cursor, decl_cursor.get_kind() != CXCursor_ParmDecl /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); + AddDeclUsages(db, decl_cursor, decl_cursor.get_kind() != CXCursor_ParmDecl /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); + optional var_type = ResolveToDeclarationType(db, decl_cursor); if (var_type.has_value()) var_def->def.variable_type = var_type.value(); @@ -570,7 +630,7 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { // We don't actually need to know the return type, but we need to mark it // as an interesting usage. - ResolveDeclToType(db, decl_cursor, true /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); + AddDeclUsages(db, decl_cursor, true /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); //TypeResolution ret_type = ResolveToType(db, decl_cursor.get_type().get_return_type()); //if (ret_type.resolved_type) @@ -592,7 +652,7 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { case CXCursor_ParmDecl: // We don't need to know the arg type, but we do want to mark it as // an interesting usage. - ResolveDeclToType(db, arg, true /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); + AddDeclUsages(db, arg, true /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); //TypeResolution arg_type = ResolveToType(db, arg.get_type()); //if (arg_type.resolved_type) @@ -639,7 +699,9 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { case CXIdxEntity_Typedef: case CXIdxEntity_CXXTypeAlias: { - optional alias_of = ResolveDeclToType(db, decl->cursor, true /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); + // Note we want to fetch the first TypeRef. Running ResolveCursorType(decl->cursor) would return + // the type of the typedef/using, not the type of the referenced type. + optional alias_of = AddDeclUsages(db, decl->cursor, true /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); TypeId type_id = db->ToTypeId(decl->entityInfo->USR); IndexedTypeDef* type_def = db->Resolve(type_id); @@ -702,7 +764,8 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { for (unsigned int i = 0; i < class_info->numBases; ++i) { const CXIdxBaseClassInfo* base_class = class_info->bases[i]; - optional parent_type_id = ResolveDeclToType(db, base_class->cursor, true /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); + AddDeclUsages(db, base_class->cursor, true /*is_interesting*/, decl->semanticContainer, decl->lexicalContainer); + optional parent_type_id = ResolveToDeclarationType(db, base_class->cursor); IndexedTypeDef* type_def = db->Resolve(type_id); // type_def ptr could be invalidated by ResolveDeclToType. if (parent_type_id) { IndexedTypeDef* parent_type_def = db->Resolve(parent_type_id.value()); @@ -749,9 +812,13 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re case CXIdxEntity_Variable: case CXIdxEntity_Field: { - VarId var_id = db->ToVarId(ref->referencedEntity->cursor); + clang::Cursor referenced = ref->referencedEntity->cursor; + referenced = referenced.template_specialization_to_template_definition(); + + VarId var_id = db->ToVarId(referenced.get_usr()); IndexedVarDef* var_def = db->Resolve(var_id); - var_def->uses.push_back(db->id_cache.Resolve(ref->loc, false /*interesting*/)); + Location loc = db->id_cache.Resolve(ref->loc, false /*interesting*/); + var_def->uses.push_back(loc); break; } @@ -805,9 +872,13 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re Location our_loc = db->id_cache.Resolve(ref->loc, true /*is_interesting*/); if (!parent_loc.IsEqualTo(our_loc)) { IndexedFuncDef* called_def = db->Resolve(called_id); + // I suspect it is possible for the declaring type to be null + // when the class is invalid. + //if (called_def->def.declaring_type) { assert(called_def->def.declaring_type.has_value()); IndexedTypeDef* type_def = db->Resolve(called_def->def.declaring_type.value()); type_def->AddUsage(our_loc); + //} } } break; @@ -820,7 +891,10 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re case CXIdxEntity_Struct: case CXIdxEntity_CXXClass: { - TypeId referenced_id = db->ToTypeId(ref->referencedEntity->USR); + clang::Cursor referenced = ref->referencedEntity->cursor; + referenced = referenced.template_specialization_to_template_definition(); + TypeId referenced_id = db->ToTypeId(referenced.get_usr()); + IndexedTypeDef* referenced_def = db->Resolve(referenced_id); // We will not get a declaration visit for forward declared types. Try to mark them as non-bad @@ -895,7 +969,7 @@ IndexedFile Parse(std::string filename, std::vector args, bool dump NamespaceHelper ns; IndexParam param(&db, &ns); clang_indexTranslationUnit(index_action, ¶m, callbacks, sizeof(callbacks), - CXIndexOpt_IndexFunctionLocalSymbols | CXIndexOpt_SkipParsedBodiesInSession, tu.cx_tu); + CXIndexOpt_IndexFunctionLocalSymbols | CXIndexOpt_SkipParsedBodiesInSession | CXIndexOpt_IndexImplicitTemplateInstantiations, tu.cx_tu); clang_IndexAction_dispose(index_action); diff --git a/libclangmm/Cursor.cc b/libclangmm/Cursor.cc index 788b7e8f..586ad76b 100644 --- a/libclangmm/Cursor.cc +++ b/libclangmm/Cursor.cc @@ -27,6 +27,10 @@ bool Type::is_fundamental() const { cx_type.kind <= CXType_LastBuiltin; } +CXCursor Type::get_declaration() const { + return clang_getTypeDeclaration(cx_type); +} + std::string Type::get_usr() const { return clang::Cursor(clang_getTypeDeclaration(cx_type)).get_usr(); } @@ -133,9 +137,10 @@ bool Cursor::is_definition() const { } Cursor Cursor::template_specialization_to_template_definition() const { - // TODO: Should we return this same cursor if this is not a template? We - // can probably check USR to do that. - return clang_getSpecializedCursorTemplate(cx_cursor); + CXCursor definition = clang_getSpecializedCursorTemplate(cx_cursor); + if (definition.kind == CXCursor_FirstInvalid) + return cx_cursor; + return definition; } Cursor Cursor::get_referenced() const { diff --git a/libclangmm/Cursor.h b/libclangmm/Cursor.h index 69b8b476..c6d080b6 100644 --- a/libclangmm/Cursor.h +++ b/libclangmm/Cursor.h @@ -21,6 +21,8 @@ public: // Returns true if this is a fundamental type like int. bool is_fundamental() const; + // clang::Cursor is not defined so we have to return CXCursor + CXCursor get_declaration() const; std::string get_usr() const; std::string get_spelling() const; @@ -58,7 +60,14 @@ public: bool is_definition() const; + // If the given cursor points to a template specialization, this + // will return the cursor pointing to the template definition. + // If the given cursor is not a template specialization, this will + // just return the same cursor. + // + // This means it is always safe to call this method. Cursor template_specialization_to_template_definition() const; + Cursor get_referenced() const; Cursor get_canonical() const; Cursor get_definition() const; diff --git a/test.cc b/test.cc index 04de9b7a..b15d40ff 100644 --- a/test.cc +++ b/test.cc @@ -82,7 +82,7 @@ void VerifySerializeToFrom(IndexedFile* file) { } } -int main222(int argc, char** argv) { +int main(int argc, char** argv) { // TODO: Assert that we need to be on clang >= 3.9.1 /* @@ -94,8 +94,12 @@ int main222(int argc, char** argv) { */ for (std::string path : GetFilesInFolder("tests", true /*add_folder_to_path*/)) { - //if (path != "tests/usage/type_usage_declare_field.cc") continue; - path = "C:/Users/jacob/Desktop/superindex/indexer/" + path; + //if (path == "tests/foobar.cc") continue; + //if (path == "tests/inheritance/class_inherit_templated_parent.cc") continue; + //if (path != "tests/templates/template_class_type_usage_folded_into_one.cc") continue; + + //if (path != "tests/templates/template_class_type_usage_folded_into_one.cc") continue; + //path = "C:/Users/jacob/Desktop/superindex/indexer/" + path; // Parse expected output from the test, parse it into JSON document. std::string expected_output; @@ -105,7 +109,7 @@ int main222(int argc, char** argv) { // Run test. std::cout << "[START] " << path << std::endl; - IndexedFile db = Parse(path, {}, true /*dump_ast*/); + IndexedFile db = Parse(path, {}, false /*dump_ast*/); VerifySerializeToFrom(&db); std::string actual_output = db.ToString(); diff --git a/tests/enums/enum_class_decl.cc b/tests/enums/enum_class_decl.cc index 1626f041..3a4b176e 100644 --- a/tests/enums/enum_class_decl.cc +++ b/tests/enums/enum_class_decl.cc @@ -22,6 +22,7 @@ OUTPUT: "short_name": "A", "qualified_name": "Foo::A", "definition": "1:2:3", + "variable_type": 0, "declaring_type": 0, "uses": ["1:2:3"] }, { @@ -30,6 +31,7 @@ OUTPUT: "short_name": "B", "qualified_name": "Foo::B", "definition": "1:3:3", + "variable_type": 0, "declaring_type": 0, "uses": ["1:3:3"] }] diff --git a/tests/enums/enum_decl.cc b/tests/enums/enum_decl.cc index 82dc251e..9b074d8b 100644 --- a/tests/enums/enum_decl.cc +++ b/tests/enums/enum_decl.cc @@ -22,6 +22,7 @@ OUTPUT: "short_name": "A", "qualified_name": "Foo::A", "definition": "1:2:3", + "variable_type": 0, "declaring_type": 0, "uses": ["1:2:3"] }, { @@ -30,6 +31,7 @@ OUTPUT: "short_name": "B", "qualified_name": "Foo::B", "definition": "1:3:3", + "variable_type": 0, "declaring_type": 0, "uses": ["1:3:3"] }] diff --git a/tests/enums/enum_inherit.cc b/tests/enums/enum_inherit.cc index c01af887..40794cba 100644 --- a/tests/enums/enum_inherit.cc +++ b/tests/enums/enum_inherit.cc @@ -22,6 +22,7 @@ OUTPUT: "short_name": "A", "qualified_name": "Foo::A", "definition": "1:2:3", + "variable_type": 0, "declaring_type": 0, "uses": ["1:2:3"] }, { @@ -30,6 +31,7 @@ OUTPUT: "short_name": "B", "qualified_name": "Foo::B", "definition": "1:3:3", + "variable_type": 0, "declaring_type": 0, "uses": ["1:3:3"] }] diff --git a/tests/enums/enum_usage.cc b/tests/enums/enum_usage.cc index 5b146df7..cdc6bdd4 100644 --- a/tests/enums/enum_usage.cc +++ b/tests/enums/enum_usage.cc @@ -24,6 +24,7 @@ OUTPUT: "short_name": "A", "qualified_name": "Foo::A", "definition": "1:2:3", + "variable_type": 0, "declaring_type": 0, "uses": ["1:2:3", "1:6:14"] }, { @@ -32,6 +33,7 @@ OUTPUT: "short_name": "B", "qualified_name": "Foo::B", "definition": "1:3:3", + "variable_type": 0, "declaring_type": 0, "uses": ["1:3:3"] }, { diff --git a/tests/foobar.cc b/tests/foobar.cc index a25609ba..c719bc2a 100644 --- a/tests/foobar.cc +++ b/tests/foobar.cc @@ -1,40 +1,267 @@ -void called(int a); +enum A {}; +enum B {}; -int gen() { return 1; } +template +struct Foo { + struct Inner {}; +}; + +Foo::Inner a; +Foo b; + + +#if false +// We could store how many template parameters Foo has and then skip that many TypeRefs..., +// if there was still a TypeRef after (and we are not ignoring it) then we know +// that is the variable type. + +EnumDecl A +EnumDecl B +ClassTemplate Foo + TemplateTypeParameter T + StructDecl Inner +VarDecl a + TemplateRef Foo + TypeRef enum A + TypeRef struct Foo::Inner + CallExpr Inner +VarDecl b + TemplateRef Foo + TypeRef enum B + CallExpr Foo +#endif -void foo() { - called(gen() * gen()); -} /* OUTPUT: { - "types": [], - "functions": [{ + "types": [{ "id": 0, - "usr": "c:@F@called#I#", - "short_name": "called", - "qualified_name": "called", - "declarations": ["1:1:6"], - "callers": ["2@1:6:3"], - "uses": ["1:1:6", "1:6:3"] + "usr": "c:@E@A", + "short_name": "A", + "qualified_name": "A", + "definition": "1:1:6", + "uses": ["*1:1:6", "*1:9:5"] }, { "id": 1, - "usr": "c:@F@gen#", - "short_name": "gen", - "qualified_name": "gen", - "definition": "1:3:5", - "callers": ["2@1:6:10", "2@1:6:18"], - "uses": ["1:3:5", "1:6:10", "1:6:18"] + "usr": "c:@E@B", + "short_name": "B", + "qualified_name": "B", + "definition": "1:2:6", + "uses": ["*1:2:6", "*1:10:5"] }, { "id": 2, - "usr": "c:@F@foo#", - "short_name": "foo", - "qualified_name": "foo", - "definition": "1:5:6", - "callees": ["0@1:6:3", "1@1:6:10", "1@1:6:18"], - "uses": ["1:5:6"] + "usr": "c:@ST>1#T@Foo", + "short_name": "Foo", + "qualified_name": "Foo", + "definition": "1:5:8", + "uses": ["*1:5:8", "*1:9:1", "*1:10:1"] + }, { + "id": 3, + "usr": "c:@ST>1#T@Foo@S@Inner", + "short_name": "Inner", + "qualified_name": "Foo::Inner", + "definition": "1:6:10", + "uses": ["*1:6:10", "*1:9:9"] }], - "variables": [] + "functions": [], + "variables": [{ + "id": 0, + "usr": "c:@a", + "short_name": "a", + "qualified_name": "a", + "definition": "1:9:15", + "variable_type": 3, + "uses": ["1:9:15"] + }, { + "id": 1, + "usr": "c:@b", + "short_name": "b", + "qualified_name": "b", + "definition": "1:10:8", + "variable_type": 2, + "uses": ["1:10:8"] + }] } -*/ \ No newline at end of file + +*/ + + + + + + + + + +#if false +namespace ns { + enum VarType {}; + + template + struct Holder { + static constexpr VarType static_var = (VarType)0x0; + }; + + template + const typename VarType Holder<_>::static_var; + + + int Foo = Holder::static_var; + int Foo2 = Holder::static_var; +} +#endif + +// TODO: we are not marking interesting usage for a CStyleCastExpr +// TODO: we are resoling templates in a weird way (should be 1 type) +#if false +Namespace ns + EnumDecl VarType + ClassTemplate Holder + TemplateTypeParameter _ + VarDecl static_var + TypeRef enum ns::VarType + CStyleCastExpr + TypeRef enum ns::VarType + IntegerLiteral + VarDecl static_var + TemplateTypeParameter _ + TypeRef enum ns::VarType + TemplateRef Holder + TypeRef _ + VarDecl Foo + UnexposedExpr static_var + UnexposedExpr static_var + DeclRefExpr static_var + TemplateRef Holder + VarDecl static_var + TypeRef enum ns::VarType + TemplateRef Holder +#endif + + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@N@ns@E@VarType", + "short_name": "VarType", + "qualified_name": "ns::VarType", + "definition": "1:2:8", + "uses": ["*1:2:8", "*1:6:22", "1:6:44", "*1:10:18"] + }, { + "id": 1, + "usr": "c:@N@ns@ST>1#T@Holder", + "short_name": "Holder", + "qualified_name": "ns::Holder", + "definition": "1:5:10", + "vars": [0], + "uses": ["*1:5:10", "*1:10:26", "1:13:13", "1:14:14"] + }], + "functions": [], + "variables": [{ + "id": 0, + "usr": "c:@N@ns@ST>1#T@Holder@static_var", + "short_name": "static_var", + "qualified_name": "ns::Holder::static_var", + "declaration": "1:6:30", + "definition": "1:10:37", + "variable_type": 0, + "declaring_type": 1, + "uses": ["1:6:30", "1:10:37"] + }, { + "id": 1, + "usr": "c:@N@ns@Foo", + "short_name": "Foo", + "qualified_name": "ns::Foo", + "definition": "1:13:7", + "uses": ["1:13:7"] + }, { + "id": 2, + "usr": "c:@N@ns@S@Holder>#I@static_var", + "short_name": "static_var", + "qualified_name": "static_var", + "definition": "1:10:37", + "variable_type": 0, + "declaring_type": 2, + "uses": ["1:13:26", "1:10:37", "1:14:27"] + }, { + "id": 3, + "usr": "c:@N@ns@Foo2", + "short_name": "Foo2", + "qualified_name": "ns::Foo2", + "definition": "1:14:7", + "uses": ["1:14:7"] + }] +} +*/ + + + + + + + + + + + + + + + + + + + + + + + + + +//#include +//#include + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include \ No newline at end of file diff --git a/tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc b/tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc new file mode 100644 index 00000000..99297dc7 --- /dev/null +++ b/tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc @@ -0,0 +1,51 @@ +namespace ns { + template + struct Foo { + template + static int foo() { + return 3; + } + }; + + int a = Foo::foo(); + int b = Foo::foo(); +} + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@N@ns@ST>1#T@Foo", + "short_name": "Foo", + "qualified_name": "ns::Foo", + "definition": "1:3:10", + "funcs": [0], + "uses": ["*1:3:10", "1:10:11", "1:11:11"] + }], + "functions": [{ + "id": 0, + "usr": "c:@N@ns@ST>1#T@Foo@FT@>1#Tfoo#I#S", + "short_name": "foo", + "qualified_name": "ns::Foo::foo", + "definition": "1:5:16", + "declaring_type": 0, + "uses": ["1:5:16", "1:10:21", "1:11:22"] + }], + "variables": [{ + "id": 0, + "usr": "c:@N@ns@a", + "short_name": "a", + "qualified_name": "ns::a", + "definition": "1:10:7", + "uses": ["1:10:7"] + }, { + "id": 1, + "usr": "c:@N@ns@b", + "short_name": "b", + "qualified_name": "ns::b", + "definition": "1:11:7", + "uses": ["1:11:7"] + }] +} +*/ \ No newline at end of file diff --git a/tests/templates/namespace_template_type_usage_folded_into_one.cc b/tests/templates/namespace_template_type_usage_folded_into_one.cc new file mode 100644 index 00000000..052faaf3 --- /dev/null +++ b/tests/templates/namespace_template_type_usage_folded_into_one.cc @@ -0,0 +1,39 @@ +namespace ns { + template + class Foo {}; + + Foo a; + Foo b; +} + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@N@ns@ST>1#T@Foo", + "short_name": "Foo", + "qualified_name": "ns::Foo", + "definition": "1:3:9", + "uses": ["*1:3:9", "*1:5:3", "*1:6:3"] + }], + "functions": [], + "variables": [{ + "id": 0, + "usr": "c:@N@ns@a", + "short_name": "a", + "qualified_name": "ns::a", + "definition": "1:5:12", + "variable_type": 0, + "uses": ["1:5:12"] + }, { + "id": 1, + "usr": "c:@N@ns@b", + "short_name": "b", + "qualified_name": "ns::b", + "definition": "1:6:13", + "variable_type": 0, + "uses": ["1:6:13"] + }] +} +*/ \ No newline at end of file diff --git a/tests/templates/template_class_func_usage_folded_into_one.cc b/tests/templates/template_class_func_usage_folded_into_one.cc new file mode 100644 index 00000000..3b5865ef --- /dev/null +++ b/tests/templates/template_class_func_usage_folded_into_one.cc @@ -0,0 +1,48 @@ +template +struct Foo { + static int foo() { + return 3; + } +}; + +int a = Foo::foo(); +int b = Foo::foo(); + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@ST>1#T@Foo", + "short_name": "Foo", + "qualified_name": "Foo", + "definition": "1:2:8", + "funcs": [0], + "uses": ["*1:2:8", "1:8:9", "1:9:9"] + }], + "functions": [{ + "id": 0, + "usr": "c:@ST>1#T@Foo@F@foo#S", + "short_name": "foo", + "qualified_name": "Foo::foo", + "definition": "1:3:14", + "declaring_type": 0, + "uses": ["1:3:14", "1:8:19", "1:9:20"] + }], + "variables": [{ + "id": 0, + "usr": "c:@a", + "short_name": "a", + "qualified_name": "a", + "definition": "1:8:5", + "uses": ["1:8:5"] + }, { + "id": 1, + "usr": "c:@b", + "short_name": "b", + "qualified_name": "b", + "definition": "1:9:5", + "uses": ["1:9:5"] + }] +} +*/ \ No newline at end of file diff --git a/tests/templates/template_class_template_func_usage_folded_into_one.cc b/tests/templates/template_class_template_func_usage_folded_into_one.cc new file mode 100644 index 00000000..7d641bc9 --- /dev/null +++ b/tests/templates/template_class_template_func_usage_folded_into_one.cc @@ -0,0 +1,49 @@ +template +struct Foo { + template + static int foo() { + return 3; + } +}; + +int a = Foo::foo(); +int b = Foo::foo(); + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@ST>1#T@Foo", + "short_name": "Foo", + "qualified_name": "Foo", + "definition": "1:2:8", + "funcs": [0], + "uses": ["*1:2:8", "1:9:9", "1:10:9"] + }], + "functions": [{ + "id": 0, + "usr": "c:@ST>1#T@Foo@FT@>1#Tfoo#I#S", + "short_name": "foo", + "qualified_name": "Foo::foo", + "definition": "1:4:14", + "declaring_type": 0, + "uses": ["1:4:14", "1:9:19", "1:10:20"] + }], + "variables": [{ + "id": 0, + "usr": "c:@a", + "short_name": "a", + "qualified_name": "a", + "definition": "1:9:5", + "uses": ["1:9:5"] + }, { + "id": 1, + "usr": "c:@b", + "short_name": "b", + "qualified_name": "b", + "definition": "1:10:5", + "uses": ["1:10:5"] + }] +} +*/ \ No newline at end of file diff --git a/tests/templates/template_class_type_usage_folded_into_one.cc b/tests/templates/template_class_type_usage_folded_into_one.cc new file mode 100644 index 00000000..be3c0e63 --- /dev/null +++ b/tests/templates/template_class_type_usage_folded_into_one.cc @@ -0,0 +1,81 @@ +enum A {}; +enum B {}; + +template +struct Foo { + struct Inner {}; +}; + +Foo::Inner a; +Foo::Inner b; + +#if false +EnumDecl A +EnumDecl B +ClassTemplate Foo + TemplateTypeParameter T + StructDecl Inner +VarDecl a + TemplateRef Foo + TypeRef enum A + TypeRef struct Foo::Inner + CallExpr Inner +VarDecl b + TemplateRef Foo + TypeRef enum B + TypeRef struct Foo::Inner + CallExpr Inner +#endif + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@E@A", + "short_name": "A", + "qualified_name": "A", + "definition": "1:1:6", + "uses": ["*1:1:6", "*1:9:5"] + }, { + "id": 1, + "usr": "c:@E@B", + "short_name": "B", + "qualified_name": "B", + "definition": "1:2:6", + "uses": ["*1:2:6", "*1:10:5"] + }, { + "id": 2, + "usr": "c:@ST>1#T@Foo", + "short_name": "Foo", + "qualified_name": "Foo", + "definition": "1:5:8", + "uses": ["*1:5:8", "*1:9:1", "*1:10:1"] + }, { + "id": 3, + "usr": "c:@ST>1#T@Foo@S@Inner", + "short_name": "Inner", + "qualified_name": "Foo::Inner", + "definition": "1:6:10", + "uses": ["*1:6:10", "*1:9:9", "*1:10:9"] + }], + "functions": [], + "variables": [{ + "id": 0, + "usr": "c:@a", + "short_name": "a", + "qualified_name": "a", + "definition": "1:9:15", + "variable_type": 3, + "uses": ["1:9:15"] + }, { + "id": 1, + "usr": "c:@b", + "short_name": "b", + "qualified_name": "b", + "definition": "1:10:15", + "variable_type": 3, + "uses": ["1:10:15"] + }] +} +*/ \ No newline at end of file diff --git a/tests/templates/template_class_var_usage_folded_into_one.cc b/tests/templates/template_class_var_usage_folded_into_one.cc new file mode 100644 index 00000000..26cf3b82 --- /dev/null +++ b/tests/templates/template_class_var_usage_folded_into_one.cc @@ -0,0 +1,44 @@ +template +struct Foo { + static constexpr int var = 3; +}; + +int a = Foo::var; +int b = Foo::var; + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@ST>1#T@Foo", + "short_name": "Foo", + "qualified_name": "Foo", + "definition": "1:2:8", + "uses": ["*1:2:8", "1:6:9", "1:7:9"] + }], + "functions": [], + "variables": [{ + "id": 0, + "usr": "c:@ST>1#T@Foo@var", + "short_name": "var", + "qualified_name": "Foo::var", + "declaration": "1:3:24", + "uses": ["1:3:24", "1:6:19", "1:7:20"] + }, { + "id": 1, + "usr": "c:@a", + "short_name": "a", + "qualified_name": "a", + "definition": "1:6:5", + "uses": ["1:6:5"] + }, { + "id": 2, + "usr": "c:@b", + "short_name": "b", + "qualified_name": "b", + "definition": "1:7:5", + "uses": ["1:7:5"] + }] +} +*/ \ No newline at end of file diff --git a/tests/templates/template_func_usage_folded_into_one.cc b/tests/templates/template_func_usage_folded_into_one.cc new file mode 100644 index 00000000..4e7ff3eb --- /dev/null +++ b/tests/templates/template_func_usage_folded_into_one.cc @@ -0,0 +1,40 @@ +template +static int foo() { + return 3; +} + +int a = foo(); +int b = foo(); + +// TODO: put template foo inside a namespace +// TODO: put template foo inside a template class inside a namespace + +/* +OUTPUT: +{ + "types": [], + "functions": [{ + "id": 0, + "usr": "c:template_func_usage_folded_into_one.cc@FT@>1#Tfoo#I#", + "short_name": "foo", + "qualified_name": "foo", + "definition": "1:2:12", + "uses": ["1:2:12", "1:6:9", "1:7:9"] + }], + "variables": [{ + "id": 0, + "usr": "c:@a", + "short_name": "a", + "qualified_name": "a", + "definition": "1:6:5", + "uses": ["1:6:5"] + }, { + "id": 1, + "usr": "c:@b", + "short_name": "b", + "qualified_name": "b", + "definition": "1:7:5", + "uses": ["1:7:5"] + }] +} +*/ \ No newline at end of file diff --git a/tests/templates/template_type_usage_folded_into_one.cc b/tests/templates/template_type_usage_folded_into_one.cc new file mode 100644 index 00000000..fff53418 --- /dev/null +++ b/tests/templates/template_type_usage_folded_into_one.cc @@ -0,0 +1,37 @@ +template +class Foo {}; + +Foo a; +Foo b; + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@ST>1#T@Foo", + "short_name": "Foo", + "qualified_name": "Foo", + "definition": "1:2:7", + "uses": ["*1:2:7", "*1:4:1", "*1:5:1"] + }], + "functions": [], + "variables": [{ + "id": 0, + "usr": "c:@a", + "short_name": "a", + "qualified_name": "a", + "definition": "1:4:10", + "variable_type": 0, + "uses": ["1:4:10"] + }, { + "id": 1, + "usr": "c:@b", + "short_name": "b", + "qualified_name": "b", + "definition": "1:5:11", + "variable_type": 0, + "uses": ["1:5:11"] + }] +} +*/ \ No newline at end of file diff --git a/tests/templates/template_var_usage_folded_into_one.cc b/tests/templates/template_var_usage_folded_into_one.cc new file mode 100644 index 00000000..0bba0c05 --- /dev/null +++ b/tests/templates/template_var_usage_folded_into_one.cc @@ -0,0 +1,73 @@ +enum A {}; +enum B {}; + +template +T var = 3; + +int a = var; +int b = var; + +// TODO: No usages of types on var. +// libclang doesn't expose the info. File a bug. + +#if false +EnumDecl A +EnumDecl B +UnexposedDecl var +VarDecl a + UnexposedExpr var + UnexposedExpr var + DeclRefExpr var + TypeRef enum A +UnexposedDecl var +VarDecl b + UnexposedExpr var + UnexposedExpr var + DeclRefExpr var + TypeRef enum B +UnexposedDecl var +#endif + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@E@A", + "short_name": "A", + "qualified_name": "A", + "definition": "1:1:6", + "uses": ["*1:1:6", "1:7:13"] + }, { + "id": 1, + "usr": "c:@E@B", + "short_name": "B", + "qualified_name": "B", + "definition": "1:2:6", + "uses": ["*1:2:6", "1:8:13"] + }], + "functions": [], + "variables": [{ + "id": 0, + "usr": "c:@var", + "short_name": "var", + "qualified_name": "var", + "definition": "1:5:3", + "uses": ["1:5:3"] + }, { + "id": 1, + "usr": "c:@a", + "short_name": "a", + "qualified_name": "a", + "definition": "1:7:5", + "uses": ["1:7:5"] + }, { + "id": 2, + "usr": "c:@b", + "short_name": "b", + "qualified_name": "b", + "definition": "1:8:5", + "uses": ["1:8:5"] + }] +} +*/ \ No newline at end of file