diff --git a/libclangmm/Cursor.cc b/libclangmm/Cursor.cc index d1fba197..81187ddb 100644 --- a/libclangmm/Cursor.cc +++ b/libclangmm/Cursor.cc @@ -130,6 +130,12 @@ bool Cursor::is_definition() const { return clang_isCursorDefinition(cx_cursor); } +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); +} + Cursor Cursor::get_referenced() const { return Cursor(clang_getCursorReferenced(cx_cursor)); } diff --git a/libclangmm/Cursor.h b/libclangmm/Cursor.h index cf75557a..6344982a 100644 --- a/libclangmm/Cursor.h +++ b/libclangmm/Cursor.h @@ -59,6 +59,7 @@ public: bool is_definition() const; + Cursor template_specialization_to_template_definition() const; Cursor get_referenced() const; Cursor get_canonical() const; Cursor get_definition() const; diff --git a/main.cpp b/main.cpp index a63b98a0..49818a4a 100644 --- a/main.cpp +++ b/main.cpp @@ -491,617 +491,6 @@ VarDef* Resolve(Database* db, VarId id) { return Resolve(&db->files[id.file_id], id); } */ - -#if false -struct NamespaceStack { - std::vector stack; - - void Push(const std::string& ns); - void Pop(); - std::string ComputeQualifiedName( - ParsingDatabase* db, std::optional declaring_type, std::string short_name); - - static NamespaceStack kEmpty; -}; -NamespaceStack NamespaceStack::kEmpty; - -void NamespaceStack::Push(const std::string& ns) { - stack.push_back(ns); -} - -void NamespaceStack::Pop() { - stack.pop_back(); -} - -std::string NamespaceStack::ComputeQualifiedName( - ParsingDatabase* db, std::optional declaring_type, std::string short_name) { - if (declaring_type) { - TypeDef* def = db->Resolve(declaring_type.value()); - return def->qualified_name + "::" + short_name; - } - - std::string result; - for (const std::string& ns : stack) - result += ns + "::"; - result += short_name; - return result; -} - - - - - - - - - -std::optional ResolveDeclaringType(CXCursorKind kind, ParsingDatabase* db, const clang::Cursor& cursor, std::optional declaring_type) { - // Resolve the declaring type for out-of-line method definitions. - if (!declaring_type) { - clang::Cursor parent = cursor.get_semantic_parent(); - switch (parent.get_kind()) { - case CXCursor_ClassDecl: - case CXCursor_StructDecl: - declaring_type = db->ToTypeId(parent.get_usr()); - break; - } - } - - // FieldDecl, etc must have a declaring type. - assert(cursor.get_kind() != kind || declaring_type); - - return declaring_type; -} - -// |func_id| is the function definition that is currently being processed. -void InsertReference(ParsingDatabase* db, std::optional func_id, clang::Cursor referencer) { - clang::SourceLocation loc = referencer.get_source_location(); - clang::Cursor referenced = referencer.get_referenced(); - - // Try to reference the actual template, instead of a specialization. - CXCursor generic_def = clang_getSpecializedCursorTemplate(referenced.cx_cursor); - if (!clang_Cursor_isNull(generic_def)) - referenced = clang::Cursor(generic_def); - - switch (referenced.get_kind()) { - case CXCursor_Constructor: - case CXCursor_Destructor: - case CXCursor_CXXMethod: - case CXCursor_FunctionDecl: - case CXCursor_FunctionTemplate: - { - FuncId referenced_id = db->ToFuncId(referenced.get_usr()); - FuncDef* referenced_def = db->Resolve(referenced_id); - - if (func_id) { - FuncDef* func_def = db->Resolve(func_id.value()); - func_def->callees.push_back(FuncRef(referenced_id, loc)); - referenced_def->callers.push_back(FuncRef(func_id.value(), loc)); - } - - referenced_def->all_uses.push_back(loc); - break; - } - - case CXCursor_ParmDecl: - case CXCursor_FieldDecl: - case CXCursor_VarDecl: - { - VarId referenced_id = db->ToVarId(referenced.get_usr()); - VarDef* referenced_def = db->Resolve(referenced_id); - - referenced_def->all_uses.push_back(loc); - break; - } - default: - std::cerr << "Unhandled reference from \"" << referencer.ToString() - << "\" to \"" << referenced.ToString() << "\"" << std::endl; - break; - } -} - - - - - - - - - - - - - - - - -void InsertTypeUsageAtLocation(ParsingDatabase* db, clang::Type type, const clang::SourceLocation& location) { - clang::Type raw_type = type.strip_qualifiers(); - - std::string usr = raw_type.get_usr(); - if (usr == "") - return; - - // Add a usage to the type of the variable. - TypeId type_id = db->ToTypeId(raw_type.get_usr()); - db->Resolve(type_id)->interesting_uses.push_back(location); -} - -struct VarDeclVisitorParam { - ParsingDatabase* db; - std::optional func_id; - bool seen_type_ref = false; - - VarDeclVisitorParam(ParsingDatabase* db, std::optional func_id) - : db(db), func_id(func_id) {} -}; - -// NOTE: This function does not process any of the definitions/etc defined -// inside of the call initializing the variable. That should be handled -// by the function-definition visitor! -clang::VisiterResult VarDeclVisitor(clang::Cursor cursor, clang::Cursor parent, VarDeclVisitorParam* param) { - switch (cursor.get_kind()) { - case CXCursor_TemplateRef: - InsertTypeUsageAtLocation(param->db, cursor.get_referenced().get_type(), cursor.get_source_location()); - return clang::VisiterResult::Continue; - - case CXCursor_TypeRef: - // This block of code will have two TypeRef nodes: - // Foo Foo::name = 3 - // We try to avoid the second reference here by only processing the first one. - if (!param->seen_type_ref) { - param->seen_type_ref = true; - InsertTypeUsageAtLocation(param->db, cursor.get_referenced().get_type(), cursor.get_source_location()); - } - return clang::VisiterResult::Continue; - - case CXCursor_CallExpr: - case CXCursor_UnexposedExpr: - case CXCursor_UnaryOperator: - return clang::VisiterResult::Continue; - - default: - std::cerr << "VarDeclVisitor unhandled " << cursor.ToString() << std::endl; - return clang::VisiterResult::Continue; - } -} - -void HandleVarDecl(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor var, std::optional declaring_type, std::optional func_id, bool declare_variable) { - //Dump(var); - - // Add a usage to the type of the variable. - //if (var.is_definition()) - // InsertTypeUsageAtLocation(db, var.get_type(), var.get_source_location()); - - // Add usage to types. - VarDeclVisitorParam varDeclVisitorParam(db, func_id); - var.VisitChildren(&VarDeclVisitor, &varDeclVisitorParam); - - if (!declare_variable) - return; - - // Note: if there is no USR then there can be no declaring type, as all - // member variables of a class must have a name. Only function parameters - // can be nameless. - std::string var_usr = var.get_usr(); - if (var_usr.size() == 0) { - assert(var.get_kind() == CXCursor_ParmDecl); - return; - } - - VarId var_id = db->ToVarId(var_usr); - VarDef* var_def = db->Resolve(var_id); - - declaring_type = ResolveDeclaringType(CXCursor_FieldDecl, db, var, declaring_type); - if (declaring_type && !var_def->declaration) { - // Note: If USR is null there can be no declaring type. - db->Resolve(declaring_type.value())->vars.push_back(var_id); - var_def->declaring_type = declaring_type; - } - - // TODO: We could use RAII to verify we don't modify db while have a *Def - // instance alive. - var_def->short_name = var.get_spelling(); - var_def->qualified_name = - ns->ComputeQualifiedName(db, declaring_type, var_def->short_name); - - // We don't do any additional processing for non-definitions. - if (!var.is_definition()) { - var_def->declaration = var.get_source_location(); - return; - } - // If we're a definition and there hasn't been a forward decl, just assign - // declaration location to definition location. - else if (!var_def->declaration) { - var_def->declaration = var.get_source_location(); - } - - // TODO: Figure out how to scan initializations properly. We probably need - // to scan for assignment statement, or definition+ctor. - //var_def->initializations.push_back(var.get_source_location()); - clang::Type var_type = var.get_type().strip_qualifiers(); - std::string var_type_usr = var.get_type().strip_qualifiers().get_usr(); - if (var_type_usr != "") { - var_def->variable_type = db->ToTypeId(var_type_usr); - /* - for (clang::Type template_param_type : var_type.get_template_arguments()) { - std::string usr = template_param_type.get_usr(); - if (usr == "") - continue; - - //TypeId template_param_id = db->ToTypeId(usr); - InsertTypeUsageAtLocation(db, template_param_type, var.get_source_location()); - - //std::cout << template_param_type.get_usr() << std::endl; - }*/ - //VarDeclVisitorParam varDeclVisitorParam(db, func_id); - //var.VisitChildren(&VarDeclVisitor, &varDeclVisitorParam); - } - -} - - - - - -// TODO: Should we declare variables on prototypes? ie, -// -// foo(int* x); -// -// I'm inclined to say yes if we want a rename refactoring. - - -struct FuncDefinitionParam { - ParsingDatabase* db; - NamespaceStack* ns; - FuncId func_id; - bool is_definition; - bool has_return_type; - - FuncDefinitionParam(ParsingDatabase* db, NamespaceStack* ns, FuncId func_id, bool is_definition, bool has_return_type) - : db(db), ns(ns), func_id(func_id), is_definition(is_definition), has_return_type(has_return_type) {} -}; - -clang::VisiterResult VisitFuncDefinition(clang::Cursor cursor, clang::Cursor parent, FuncDefinitionParam* param) { - if (param->has_return_type) { - // Foo* Foo::Bar() {} will have two TypeRef nodes. - assert(cursor.get_kind() == CXCursor_TypeRef); - InsertTypeUsageAtLocation(param->db, cursor.get_referenced().get_type(), cursor.get_source_location()); - param->has_return_type = false; - } - - //std::cout << "VistFuncDefinition got " << cursor.ToString() << std::endl; - switch (cursor.get_kind()) { - - case CXCursor_CallExpr: - // When CallExpr points to a constructor, it does not have a child - // DeclRefExpr which also points to the constructor. Normal function calls - // (to a function of any type) look like this: - // - // CallExpr func_name - // ... (setup this pointer) - // *RefExpr func_name - // ... (setup arguments) - // - // Constructors, on the other hand, look like this: - // - // CallExpr func_name - // ... (setup arguments) - // - // We can't check the parent for a VarDecl, because a normal CallExpr could - // point to that. We simply check if the cursor references a constructor, - // and if so, insert the reference now, since it won't happen later. - if (cursor.get_referenced().get_kind() == CXCursor_Constructor) - InsertReference(param->db, param->func_id, cursor); - return clang::VisiterResult::Recurse; - - case CXCursor_MemberRefExpr: - case CXCursor_DeclRefExpr: - InsertReference(param->db, param->func_id, cursor); - return clang::VisiterResult::Recurse; - - case CXCursor_VarDecl: - case CXCursor_ParmDecl: - //std::cout << "!! Parsing var decl " << cursor.ToString() << std::endl; - HandleVarDecl(param->db, param->ns, cursor, std::nullopt, param->func_id, param->is_definition); - return clang::VisiterResult::Recurse; - - case CXCursor_ReturnStmt: - return clang::VisiterResult::Recurse; - - default: - //std::cerr << "Unhandled VisitFuncDefinition kind " << clang::ToString(cursor.get_kind()) << std::endl; - return clang::VisiterResult::Recurse; - } -} - -void HandleFunc(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor func, std::optional declaring_type) { - // What this method must process: - // - function declaration - // - function definition - // - method declaration - // - method inline definition - // - method definition - - // Resolve id before checking for is_definition so that we insert the - // function into the db even if it is only a prototype. This is needed for - // various file-level operations like outlining. - FuncId func_id = db->ToFuncId(func.get_usr()); - - // TODO: Consider skipping some of this processing if we've done it already - // (ie, parsed prototype, then parse definition). - - declaring_type = - ResolveDeclaringType(CXCursor_CXXMethod, db, func, declaring_type); - - FuncDef* func_def = db->Resolve(func_id); - - func_def->short_name = func.get_spelling(); - func_def->qualified_name = - ns->ComputeQualifiedName(db, declaring_type, func_def->short_name); - - if (declaring_type && !func_def->declaration) { - db->Resolve(declaring_type.value())->funcs.push_back(func_id); - func_def->declaring_type = declaring_type; - } - - // Don't process definition/body for declarations. - if (!func.is_definition()) { - func_def->declaration = func.get_source_location(); - - // We insert type references for arguments but don't use the normal visitor - // because that will add a definition for the variable. These are not - // "real" variables so we don't want to add definitions for them. - - // We navigate using cursor arguments so we can get location data. - /* - for (clang::Cursor arg : func.get_arguments()) { - switch (arg.get_kind()) { - case CXCursor_ParmDecl: - InsertTypeUsageAtLocation(db, arg.get_type(), arg.get_source_location()); - break; - } - } - */ - } - - if (func.is_definition()) - func_def->definition = func.get_source_location(); - - // Ignore any fundamental types for return. Note that void is a fundamental - // type. - bool has_return_type = !func.get_type().get_return_type().is_fundamental(); - - FuncDefinitionParam funcDefinitionParam(db, &NamespaceStack::kEmpty, func_id, func.is_definition(), has_return_type); - func.VisitChildren(&VisitFuncDefinition, &funcDefinitionParam); -} - - - - - - - - -struct UsingParam { - ParsingDatabase* db; - TypeId active_type; - - UsingParam(ParsingDatabase* db, TypeId active_type) - : db(db), active_type(active_type) {} -}; - -clang::VisiterResult VisitUsing(clang::Cursor cursor, clang::Cursor parent, UsingParam* param) { - ParsingDatabase* db = param->db; - - switch (cursor.get_kind()) { - case CXCursor_TypeRef: - { - TypeId source_type = db->ToTypeId(cursor.get_referenced().get_usr()); - db->Resolve(param->active_type)->alias_of = source_type; - return clang::VisiterResult::Break; - } - default: - std::cerr << "Unhandled VisitClassDecl kind " << clang::ToString(cursor.get_kind()) << std::endl; - break; - } - - return clang::VisiterResult::Continue; -} - - - - - - - -struct ClassDeclParam { - ParsingDatabase* db; - NamespaceStack* ns; - TypeId active_type; - - ClassDeclParam(ParsingDatabase* db, NamespaceStack* ns, TypeId active_type) - : db(db), ns(ns), active_type(active_type) {} -}; - -clang::VisiterResult VisitClassDecl(clang::Cursor cursor, clang::Cursor parent, ClassDeclParam* param) { - ParsingDatabase* db = param->db; - - switch (cursor.get_kind()) { - case CXCursor_CXXAccessSpecifier: - break; - - case CXCursor_Constructor: - case CXCursor_Destructor: - case CXCursor_CXXMethod: - HandleFunc(param->db, param->ns, cursor, param->active_type); - break; - - case CXCursor_FieldDecl: - case CXCursor_VarDecl: - HandleVarDecl(param->db, param->ns, cursor, param->active_type, std::nullopt, true /*declare_variable*/); - break; - - default: - std::cerr << "Unhandled VisitClassDecl kind " << clang::ToString(cursor.get_kind()) << std::endl; - break; - } - - return clang::VisiterResult::Continue; -} - -void HandleClassDecl(clang::Cursor cursor, ParsingDatabase* db, NamespaceStack* ns, bool is_alias) { - TypeId type_id = db->ToTypeId(cursor.get_usr()); - TypeDef* type_def = db->Resolve(type_id); - - type_def->short_name = cursor.get_spelling(); - // TODO: Support nested classes (pass in declaring type insteaad of nullopt!) - type_def->qualified_name = - ns->ComputeQualifiedName(db, std::nullopt, type_def->short_name); - - if (!cursor.is_definition()) { - if (!type_def->declaration) - type_def->declaration = cursor.get_source_location(); - return; - } - - type_def->definition = cursor.get_source_location(); - - if (is_alias) { - UsingParam usingParam(db, type_id); - cursor.VisitChildren(&VisitUsing, &usingParam); - } - else { - ClassDeclParam classDeclParam(db, ns, type_id); - cursor.VisitChildren(&VisitClassDecl, &classDeclParam); - } -} - - - - - - - - -struct FileParam { - ParsingDatabase* db; - NamespaceStack* ns; - - FileParam(ParsingDatabase* db, NamespaceStack* ns) : db(db), ns(ns) {} -}; - -clang::VisiterResult VisitFile(clang::Cursor cursor, clang::Cursor parent, FileParam* param) { - switch (cursor.get_kind()) { - case CXCursor_Namespace: - // For a namespace, visit the children of the namespace, but this time with - // a pushed namespace stack. - param->ns->Push(cursor.get_display_name()); - cursor.VisitChildren(&VisitFile, param); - param->ns->Pop(); - break; - - case CXCursor_TypeAliasDecl: - case CXCursor_TypedefDecl: - HandleClassDecl(cursor, param->db, param->ns, true /*is_alias*/); - break; - - case CXCursor_ClassTemplate: - case CXCursor_StructDecl: - case CXCursor_ClassDecl: - // TODO: Cleanup Handle* param order. - HandleClassDecl(cursor, param->db, param->ns, false /*is_alias*/); - break; - - case CXCursor_CXXMethod: - case CXCursor_FunctionDecl: - case CXCursor_FunctionTemplate: - HandleFunc(param->db, param->ns, cursor, std::nullopt); - break; - - case CXCursor_VarDecl: - HandleVarDecl(param->db, param->ns, cursor, std::nullopt, std::nullopt, true /*declare_variable*/); - break; - - default: - std::cerr << "Unhandled VisitFile kind " << clang::ToString(cursor.get_kind()) << std::endl; - break; - } - - return clang::VisiterResult::Continue; -} - - -ParsingDatabase Parse2(std::string filename) { - std::vector args; - - clang::Index index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/); - clang::TranslationUnit tu(index, filename, args); - - std::cout << "Start document dump" << std::endl; - Dump(tu.document_cursor()); - std::cout << "Done document dump" << std::endl << std::endl; - - ParsingDatabase db; - NamespaceStack ns; - FileParam file_param(&db, &ns); - - tu.document_cursor().VisitChildren(&VisitFile, &file_param); - return db; -} - -#endif - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1293,67 +682,7 @@ bool IsTypeDefinition(const CXIdxContainerInfo* container) { } } -#if false -struct TypeResolution { - std::optional resolved_type; - // If |check_template_arguments| is true, |original_type| may have template - // parameters with interesting usage information. - std::vector template_arguments; -}; - -TypeResolution ResolveToType(ParsingDatabase* db, clang::Type type) { - TypeResolution result; - - type = type.strip_qualifiers(); - std::string usr = type.get_usr(); - - if (usr == "") - return result; - - // TODO: Add a check and don't resolve template specializations that exist in source code. - // Resolve template specialization so that we always point to the non-specialized type. - result.template_arguments = type.get_template_arguments(); - if (result.template_arguments.size() > 0) { - clang::Cursor decl = clang_getTypeDeclaration(type.cx_type); - clang::Cursor unresolved_decl = clang_getSpecializedCursorTemplate(decl.cx_cursor); - usr = clang::Cursor(unresolved_decl).get_usr(); - /* - std::string template_usr = clang::Cursor(unresolved_decl).get_usr(); - if (template_usr != "") { - result.check_template_arguments = true; - result.original_type = type; - usr = template_usr; - } - */ - } - - result.resolved_type = db->ToTypeId(usr); - return result; -} - -clang::SourceLocation FindLocationOfTypeSpecifier(clang::Cursor cursor) { - std::cout << "FindLocationOfTypeSpecifier " << std::endl; - Dump(cursor); - - std::optional child = FindType(cursor); - assert(child.has_value()); // If this assert ever fails just use |cursor| loc or figure out what type ref we are missing. - return child.value().get_source_location(); -} - - - -void AddInterestingUsageToType(ParsingDatabase* db, TypeResolution resolved_type, clang::SourceLocation location) { - // TODO: pass cursor in. Implement custom visitor just for this. Cursor resolves type as needed. Can we use visitor types as the actual types? - - TypeDef* type_def = db->Resolve(resolved_type.resolved_type.value()); - type_def->interesting_uses.push_back(location); - - //if (resolved_type.check_template_arguments) { - - //} -} -#endif struct VisitDeclForTypeUsageParam { ParsingDatabase* db; @@ -1780,7 +1109,6 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re case CXIdxEntity_Struct: case CXIdxEntity_CXXClass: { - std::cout << "Reference at " << clang::SourceLocation(ref->loc).ToString() << std::endl; TypeId referenced_id = db->ToTypeId(ref->referencedEntity->USR); TypeDef* referenced_def = db->Resolve(referenced_id); @@ -1791,10 +1119,8 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re // not appear to be a good way to disambiguate these references, as using // parent type alone breaks other indexing tasks. // - // To work around this, we store the last type usage location. If our - // current location is the same as that location, don't report it as a - // usage. We don't need to check active type id because there can only be - // one type reference at any location in code. + // To work around this, we check to see if the usage location has been + // inserted into all_uses previously. // // struct Foo {}; // void Make() { @@ -1802,45 +1128,9 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re // } // clang::SourceLocation loc = ref->loc; - //if (param->last_type_usage_location == loc) break; - //param->last_type_usage_location = loc; - if (!HasUsage(referenced_def->all_uses, loc)) referenced_def->all_uses.push_back(loc); - /* - // - // Variable declarations have an embedded TypeRef. - // - if (cursor.get_kind() == CXCursor_TypeRef && - ref->parentEntity && ref->parentEntity->kind == CXIdxEntity_Variable) { - referenced_def->interesting_uses.push_back(loc); - } - - // - // If this is a type reference to a method then there will be two calls to - // this method with a TypeRef cursor kind. Only the return type is an - // interesting use (ie, Foo* is interesting, but not |Foo| in Foo::Hello). - // - // Foo* Foo::Hello() {} - // - // We handle this by adding a |needs_return_type_index| bool to FuncDef. - // It is only set to true when the type has a return value. We visit the - // return type TypeRef first, so we consume the bool and the second TypeRef - // will not get marked as interesting. - // - if (cursor.get_kind() == CXCursor_TypeRef && - ref->parentEntity && ref->parentEntity->kind == CXIdxEntity_CXXInstanceMethod) { - FuncId declaring_func_id = db->ToFuncId(ref->parentEntity->USR); - FuncDef* declaring_func_def = db->Resolve(declaring_func_id); - - if (declaring_func_def->needs_return_type_index) { - declaring_func_def->needs_return_type_index = false; - referenced_def->interesting_uses.push_back(loc); - } - } - */ - break; } @@ -1869,7 +1159,7 @@ ParsingDatabase Parse(std::string filename) { clang::Index index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/); clang::TranslationUnit tu(index, filename, args); - Dump(tu.document_cursor()); + //Dump(tu.document_cursor()); CXIndexAction index_action = clang_IndexAction_create(index.cx_index);