From f7e2b200591372495d93b0e56d00bce3eafe89c9 Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Sun, 5 Mar 2017 22:23:41 -0800 Subject: [PATCH] better specialized function definition indexing --- indexer.cpp | 178 +++++++++--------- test.cc | 3 +- .../templates/specialized_func_definition.cc | 39 ++++ 3 files changed, 135 insertions(+), 85 deletions(-) create mode 100644 tests/templates/specialized_func_definition.cc diff --git a/indexer.cpp b/indexer.cpp index 6a90313f..224d2c06 100644 --- a/indexer.cpp +++ b/indexer.cpp @@ -540,15 +540,16 @@ optional AddDeclUsages(IndexedFile* db, clang::Cursor decl_cursor, VisitDeclForTypeUsageVisitorHandler(param.previous_cursor.value(), ¶m); } else { - // If we are not processing the last type ref, it *must* be a TypeRef (ie, - // and not a TemplateRef). + // If we are not processing the last type ref, it *must* be a TypeRef or + // TemplateRef. // // 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); + (param.previous_cursor.value().get_kind() == CXCursor_TypeRef || + param.previous_cursor.value().get_kind() == CXCursor_TemplateRef)); } return param.initial_type; @@ -632,105 +633,114 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { case CXIdxEntity_CXXConversionFunction: { clang::Cursor decl_cursor = decl->cursor; - FuncId func_id = db->ToFuncId(decl->entityInfo->USR); + clang::Cursor resolved = decl_cursor.template_specialization_to_template_definition(); + + FuncId func_id = db->ToFuncId(resolved.cx_cursor); IndexedFuncDef* func_def = db->Resolve(func_id); - func_def->is_bad_def = is_system_def; - - // TODO: Eventually run with this if. Right now I want to iron out bugs this may shadow. - //if (!decl->isRedeclaration) { - func_def->def.short_name = decl->entityInfo->name; - func_def->def.qualified_name = ns->QualifiedName(decl->semanticContainer, func_def->def.short_name); - //} - Location decl_loc = db->id_cache.Resolve(decl->loc, false /*interesting*/); - if (decl->isDefinition) - func_def->def.definition = decl_loc; - else - func_def->declarations.push_back(decl_loc); + func_def->uses.push_back(decl_loc); - - bool is_pure_virtual = clang_CXXMethod_isPureVirtual(decl->cursor); - bool is_ctor_or_dtor = decl->entityInfo->kind == CXIdxEntity_CXXConstructor || decl->entityInfo->kind == CXIdxEntity_CXXDestructor; - //bool process_declaring_type = is_pure_virtual || is_ctor_or_dtor; - - // Add function usage information. We only want to do it once per - // definition/declaration. Do it on definition since there should only ever - // be one of those in the entire program. - if (IsTypeDefinition(decl->semanticContainer)) { - TypeId declaring_type_id = db->ToTypeId(decl->semanticContainer->cursor); - IndexedTypeDef* declaring_type_def = db->Resolve(declaring_type_id); - func_def->def.declaring_type = declaring_type_id; - - // Mark a type reference at the ctor/dtor location. - // TODO: Should it be interesting? - if (is_ctor_or_dtor) { - Location type_usage_loc = decl_loc; - declaring_type_def->AddUsage(type_usage_loc); - } - - // Register function in declaring type if it hasn't been registered yet. - if (!Contains(declaring_type_def->def.funcs, func_id)) - declaring_type_def->def.funcs.push_back(func_id); - } - - - // We don't actually need to know the return type, but we need to mark it // as an interesting usage. 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) - // AddInterestingUsageToType(db, ret_type, FindLocationOfTypeSpecifier(decl_cursor)); + // TODO: support multiple definitions per function; right now we are hacking the 'declarations' field by + // adding a definition when we really don't have one. + if (decl->isDefinition && !func_def->def.definition.has_value()) + func_def->def.definition = decl_loc; + else + func_def->declarations.push_back(decl_loc); - if (decl->isDefinition || is_pure_virtual) { - // Mark type usage for parameters as interesting. We handle this here - // instead of inside var declaration because clang will not emit a var - // declaration for an unnamed parameter, but we still want to mark the - // usage as interesting. - // TODO: Do a similar thing for function decl parameter usages. Mark - // prototype params as interesting type usages but also relate mark - // them as as usages on the primary variable - requires USR to be - // the same. We can work around it by declaring which variables a - // parameter has declared and update the USR in the definition. - clang::Cursor cursor = decl->cursor; - for (clang::Cursor arg : cursor.get_arguments()) { - switch (arg.get_kind()) { - case CXCursor_ParmDecl: - // We don't need to know the arg type, but we do want to mark it as - // an interesting usage. Note that we use semanticContainer twice - // because a parameter is not really part of the lexical container. - AddDeclUsages(db, arg, true /*is_interesting*/, decl->semanticContainer, decl->semanticContainer); + // If decl_cursor != resolved, then decl_cursor is a template specialization. We + // don't want to override a lot of the function definition information in that + // scenario. + if (decl_cursor == resolved) { + func_def->is_bad_def = is_system_def; - //TypeResolution arg_type = ResolveToType(db, arg.get_type()); - //if (arg_type.resolved_type) - // AddInterestingUsageToType(db, arg_type, FindLocationOfTypeSpecifier(arg)); - break; + // TODO: Eventually run with this if. Right now I want to iron out bugs this may shadow. + //if (!decl->isRedeclaration) { + func_def->def.short_name = decl->entityInfo->name; + func_def->def.qualified_name = ns->QualifiedName(decl->semanticContainer, func_def->def.short_name); + //} + + bool is_pure_virtual = clang_CXXMethod_isPureVirtual(decl->cursor); + bool is_ctor_or_dtor = decl->entityInfo->kind == CXIdxEntity_CXXConstructor || decl->entityInfo->kind == CXIdxEntity_CXXDestructor; + //bool process_declaring_type = is_pure_virtual || is_ctor_or_dtor; + + // Add function usage information. We only want to do it once per + // definition/declaration. Do it on definition since there should only ever + // be one of those in the entire program. + if (IsTypeDefinition(decl->semanticContainer)) { + TypeId declaring_type_id = db->ToTypeId(decl->semanticContainer->cursor); + IndexedTypeDef* declaring_type_def = db->Resolve(declaring_type_id); + func_def->def.declaring_type = declaring_type_id; + + // Mark a type reference at the ctor/dtor location. + // TODO: Should it be interesting? + if (is_ctor_or_dtor) { + Location type_usage_loc = decl_loc; + declaring_type_def->AddUsage(type_usage_loc); } + + // Register function in declaring type if it hasn't been registered yet. + if (!Contains(declaring_type_def->def.funcs, func_id)) + declaring_type_def->def.funcs.push_back(func_id); } - // Process inheritance. - //void clang_getOverriddenCursors(CXCursor cursor, CXCursor **overridden, unsigned *num_overridden); - //void clang_disposeOverriddenCursors(CXCursor *overridden); - if (clang_CXXMethod_isVirtual(decl->cursor)) { - CXCursor* overridden; - unsigned int num_overridden; - clang_getOverriddenCursors(decl->cursor, &overridden, &num_overridden); + //TypeResolution ret_type = ResolveToType(db, decl_cursor.get_type().get_return_type()); + //if (ret_type.resolved_type) + // AddInterestingUsageToType(db, ret_type, FindLocationOfTypeSpecifier(decl_cursor)); - // TODO: How to handle multiple parent overrides?? - for (unsigned int i = 0; i < num_overridden; ++i) { - clang::Cursor parent = overridden[i]; - FuncId parent_id = db->ToFuncId(parent.get_usr()); - IndexedFuncDef* parent_def = db->Resolve(parent_id); - func_def = db->Resolve(func_id); // ToFuncId invalidated func_def + if (decl->isDefinition || is_pure_virtual) { + // Mark type usage for parameters as interesting. We handle this here + // instead of inside var declaration because clang will not emit a var + // declaration for an unnamed parameter, but we still want to mark the + // usage as interesting. + // TODO: Do a similar thing for function decl parameter usages. Mark + // prototype params as interesting type usages but also relate mark + // them as as usages on the primary variable - requires USR to be + // the same. We can work around it by declaring which variables a + // parameter has declared and update the USR in the definition. + clang::Cursor cursor = decl->cursor; + for (clang::Cursor arg : cursor.get_arguments()) { + switch (arg.get_kind()) { + case CXCursor_ParmDecl: + // We don't need to know the arg type, but we do want to mark it as + // an interesting usage. Note that we use semanticContainer twice + // because a parameter is not really part of the lexical container. + AddDeclUsages(db, arg, true /*is_interesting*/, decl->semanticContainer, decl->semanticContainer); - func_def->def.base = parent_id; - parent_def->derived.push_back(func_id); + //TypeResolution arg_type = ResolveToType(db, arg.get_type()); + //if (arg_type.resolved_type) + // AddInterestingUsageToType(db, arg_type, FindLocationOfTypeSpecifier(arg)); + break; + } } - clang_disposeOverriddenCursors(overridden); + + // Process inheritance. + //void clang_getOverriddenCursors(CXCursor cursor, CXCursor **overridden, unsigned *num_overridden); + //void clang_disposeOverriddenCursors(CXCursor *overridden); + if (clang_CXXMethod_isVirtual(decl->cursor)) { + CXCursor* overridden; + unsigned int num_overridden; + clang_getOverriddenCursors(decl->cursor, &overridden, &num_overridden); + + // TODO: How to handle multiple parent overrides?? + for (unsigned int i = 0; i < num_overridden; ++i) { + clang::Cursor parent = overridden[i]; + FuncId parent_id = db->ToFuncId(parent.get_usr()); + IndexedFuncDef* parent_def = db->Resolve(parent_id); + func_def = db->Resolve(func_id); // ToFuncId invalidated func_def + + func_def->def.base = parent_id; + parent_def->derived.push_back(func_id); + } + + clang_disposeOverriddenCursors(overridden); + } } } diff --git a/test.cc b/test.cc index 100e57ae..b7d8eee7 100644 --- a/test.cc +++ b/test.cc @@ -94,9 +94,10 @@ int main(int argc, char** argv) { */ for (std::string path : GetFilesInFolder("tests", true /*add_folder_to_path*/)) { - //if (path != "tests/templates/func_specialized_template_param.cc") continue; + //if (path != "tests/templates/specialized_func_definition.cc") continue; //if (path != "tests/constructors/invalid_reference.cc") continue; //if (path == "tests/inheritance/class_inherit_templated_parent.cc") continue; + //if (path != "tests/foobar.cc") continue; if (path == "tests/stl.cc") continue; //if (path != "tests/templates/template_class_type_usage_folded_into_one.cc") continue; diff --git a/tests/templates/specialized_func_definition.cc b/tests/templates/specialized_func_definition.cc new file mode 100644 index 00000000..da109d4c --- /dev/null +++ b/tests/templates/specialized_func_definition.cc @@ -0,0 +1,39 @@ +template +class Template { + void Foo(); +}; + +template +void Template::Foo() {} + +void Template::Foo() {} + + +/* +// TODO: usage information on Template is bad. +// TODO: Foo() should have multiple definitions. + +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@ST>1#T@Template", + "short_name": "Template", + "qualified_name": "Template", + "definition": "1:2:7", + "funcs": [0], + "uses": ["*1:2:7", "*1:7:6", "1:9:6"] + }], + "functions": [{ + "id": 0, + "usr": "c:@ST>1#T@Template@F@Foo#", + "short_name": "Foo", + "qualified_name": "Template::Foo", + "declarations": ["1:3:8", "1:9:22"], + "definition": "1:7:19", + "declaring_type": 0, + "uses": ["1:3:8", "1:7:19", "1:9:22"] + }], + "variables": [] +} +*/ \ No newline at end of file