diff --git a/libclangmm/Cursor.cc b/libclangmm/Cursor.cc index b08a3aab..d1fba197 100644 --- a/libclangmm/Cursor.cc +++ b/libclangmm/Cursor.cc @@ -67,6 +67,23 @@ std::vector Type::get_arguments() const { } +std::vector Type::get_template_arguments() const { + /* + CINDEX_LINKAGE int clang_Type_getNumTemplateArguments(CXType T); + CINDEX_LINKAGE CXType clang_Type_getTemplateArgumentAsType(CXType T, unsigned i); + */ + + int size = clang_Type_getNumTemplateArguments(cx_type); + assert(size >= 0); + if (size < 0) + return std::vector(); + + std::vector types(size); + for (int i = 0; i < size; ++i) + types.emplace_back(clang_Type_getTemplateArgumentAsType(cx_type, i)); + return types; +} + static_assert(sizeof(Cursor) == sizeof(CXCursor), "Cursor must be the same size as CXCursor"); diff --git a/libclangmm/Cursor.h b/libclangmm/Cursor.h index be7b74c0..acd4c11c 100644 --- a/libclangmm/Cursor.h +++ b/libclangmm/Cursor.h @@ -30,6 +30,7 @@ public: Type get_return_type() const; std::vector get_arguments() const; + std::vector get_template_arguments() const; CXType cx_type; }; diff --git a/main.cpp b/main.cpp index e8cf27ee..328d4517 100644 --- a/main.cpp +++ b/main.cpp @@ -516,7 +516,44 @@ std::optional ResolveDeclaringType(CXCursorKind kind, ParsingDatabase* d 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(); + switch (referenced.get_kind()) { + case CXCursor_CXXMethod: + case CXCursor_FunctionDecl: + { + 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->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->uses.push_back(loc); + break; + } + default: + std::cerr << "Unhandled reference from \"" << referencer.ToString() + << "\" to \"" << referenced.ToString() << "\"" << std::endl; + break; + } +} @@ -568,7 +605,34 @@ void InsertTypeUsageAtLocation(ParsingDatabase* db, clang::Type type, const clan db->Resolve(type_id)->uses.push_back(location); } -void HandleVarDecl(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor var, std::optional declaring_type) { +struct VarDeclVisitorParam { + ParsingDatabase* db; + std::optional func_id; + + VarDeclVisitorParam(ParsingDatabase* db, std::optional func_id) + : db(db), func_id(func_id) {} +}; + +clang::VisiterResult VarDeclVisitor(clang::Cursor cursor, clang::Cursor parent, VarDeclVisitorParam* param) { + switch (cursor.get_kind()) { + case CXCursor_TemplateRef: + case CXCursor_TypeRef: + InsertTypeUsageAtLocation(param->db, cursor.get_referenced().get_type(), cursor.get_source_location()); + break; + + case CXCursor_CallExpr: + InsertReference(param->db, param->func_id, cursor); + break; + + default: + std::cerr << "VarDeclVisitor unhandled " << cursor.ToString() << std::endl; + break; + } + + return clang::VisiterResult::Continue; +} + +void HandleVarDecl(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor var, std::optional declaring_type, std::optional func_id) { //Dump(var); // Add a usage to the type of the variable. @@ -613,51 +677,30 @@ void HandleVarDecl(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor var, s // 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 != "") + 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()); - - - -// |func_id| is the function definition that is currently being processed. -void InsertReference(ParsingDatabase* db, FuncId func_id, clang::Cursor referencer) { - clang::SourceLocation loc = referencer.get_source_location(); - clang::Cursor referenced = referencer.get_referenced(); - - switch (referenced.get_kind()) { - case CXCursor_CXXMethod: - case CXCursor_FunctionDecl: - { - FuncId referenced_id = db->ToFuncId(referenced.get_usr()); - FuncDef* referenced_def = db->Resolve(referenced_id); - FuncDef* func_def = db->Resolve(func_id); - - func_def->callees.push_back(FuncRef(referenced_id, loc)); - referenced_def->callers.push_back(FuncRef(func_id, loc)); - referenced_def->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->uses.push_back(loc); - break; - } - default: - std::cerr << "Unhandled reference from \"" << referencer.ToString() - << "\" to \"" << referenced.ToString() << "\"" << std::endl; - break; + //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); @@ -698,8 +741,8 @@ clang::VisiterResult VisitFuncDefinition(clang::Cursor cursor, clang::Cursor par case CXCursor_VarDecl: case CXCursor_ParmDecl: - HandleVarDecl(param->db, param->ns, cursor, std::nullopt); - return clang::VisiterResult::Recurse; + HandleVarDecl(param->db, param->ns, cursor, std::nullopt, param->func_id); + return clang::VisiterResult::Continue; case CXCursor_ReturnStmt: return clang::VisiterResult::Recurse; @@ -770,6 +813,12 @@ void HandleFunc(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor func, std } + + + + + + struct UsingParam { ParsingDatabase* db; TypeId active_type; @@ -798,6 +847,10 @@ clang::VisiterResult VisitUsing(clang::Cursor cursor, clang::Cursor parent, Usin + + + + struct ClassDeclParam { ParsingDatabase* db; NamespaceStack* ns; @@ -820,7 +873,7 @@ clang::VisiterResult VisitClassDecl(clang::Cursor cursor, clang::Cursor parent, case CXCursor_FieldDecl: case CXCursor_VarDecl: - HandleVarDecl(param->db, param->ns, cursor, param->active_type); + HandleVarDecl(param->db, param->ns, cursor, param->active_type, std::nullopt); break; default: @@ -887,6 +940,7 @@ clang::VisiterResult VisitFile(clang::Cursor cursor, clang::Cursor parent, FileP HandleClassDecl(cursor, param->db, param->ns, true /*is_alias*/); break; + case CXCursor_ClassTemplate: case CXCursor_StructDecl: case CXCursor_ClassDecl: // TODO: Cleanup Handle* param order. @@ -899,7 +953,7 @@ clang::VisiterResult VisitFile(clang::Cursor cursor, clang::Cursor parent, FileP break; case CXCursor_VarDecl: - HandleVarDecl(param->db, param->ns, cursor, std::nullopt); + HandleVarDecl(param->db, param->ns, cursor, std::nullopt, std::nullopt); break; default: @@ -1025,7 +1079,7 @@ void DiffDocuments(rapidjson::Document& expected, rapidjson::Document& actual) { int main(int argc, char** argv) { for (std::string path : GetFilesInFolder("tests")) { // TODO: Fix all existing tests. - //if (path != "tests/usage/var_usage_func_parameter.cc") continue; + if (path != "tests/usage/type_usage_as_template_parameter.cc") continue; // Parse expected output from the test, parse it into JSON document. std::string expected_output; diff --git a/tests/usage/type_usage_as_template_parameter.cc b/tests/usage/type_usage_as_template_parameter.cc index 8b09c42c..bce6918c 100644 --- a/tests/usage/type_usage_as_template_parameter.cc +++ b/tests/usage/type_usage_as_template_parameter.cc @@ -1,9 +1,88 @@ -// TODO: implement this test. It is meant to support unique_ptr +template +class unique_ptr { +public: + T value; +}; + +struct Foo { + int x; +}; + +void foo() { + unique_ptr f0; + unique_ptr f1; + + f0.value.x += 5; +} + /* OUTPUT: { - "types": [], - "functions": [], - "variables": [] + "types": [{ + "id": 0, + "usr": "c:@ST>1#T@unique_ptr", + "short_name": "unique_ptr", + "qualified_name": "unique_ptr", + "definition": "tests/usage/type_usage_as_template_parameter.cc:2:7", + "vars": [0] + }, { + "id": 1, + "usr": "c:@S@Foo", + "short_name": "Foo", + "qualified_name": "Foo", + "definition": "tests/usage/type_usage_as_template_parameter.cc:7:8", + "vars": [1], + "uses": ["tests/usage/type_usage_as_template_parameter.cc:12:14", "tests/usage/type_usage_as_template_parameter.cc:13:14"] + }, { + "id": 2, + "usr": "c:@S@unique_ptr>#$@S@Foo", + "uses": ["tests/usage/type_usage_as_template_parameter.cc:12:19", "tests/usage/type_usage_as_template_parameter.cc:13:19"] + }], + "functions": [{ + "id": 0, + "usr": "c:@F@foo#", + "short_name": "foo", + "qualified_name": "foo", + "definition": "tests/usage/type_usage_as_template_parameter.cc:11:6" + }], + "variables": [{ + "id": 0, + "usr": "c:@ST>1#T@unique_ptr@FI@value", + "short_name": "value", + "qualified_name": "unique_ptr::value", + "declaration": "tests/usage/type_usage_as_template_parameter.cc:4:5", + "initializations": ["tests/usage/type_usage_as_template_parameter.cc:4:5"], + "declaring_type": 0 + }, { + "id": 1, + "usr": "c:@S@Foo@FI@x", + "short_name": "x", + "qualified_name": "Foo::x", + "declaration": "tests/usage/type_usage_as_template_parameter.cc:8:7", + "initializations": ["tests/usage/type_usage_as_template_parameter.cc:8:7"], + "declaring_type": 1, + "uses": ["tests/usage/type_usage_as_template_parameter.cc:15:12"] + }, { + "id": 2, + "usr": "c:type_usage_as_template_parameter.cc@115@F@foo#@f0", + "short_name": "f0", + "qualified_name": "f0", + "declaration": "tests/usage/type_usage_as_template_parameter.cc:12:19", + "initializations": ["tests/usage/type_usage_as_template_parameter.cc:12:19"], + "variable_type": 2, + "uses": ["tests/usage/type_usage_as_template_parameter.cc:15:3"] + }, { + "id": 3, + "usr": "c:type_usage_as_template_parameter.cc@138@F@foo#@f1", + "short_name": "f1", + "qualified_name": "f1", + "declaration": "tests/usage/type_usage_as_template_parameter.cc:13:19", + "initializations": ["tests/usage/type_usage_as_template_parameter.cc:13:19"], + "variable_type": 2 + }, { + "id": 4, + "usr": "c:@S@unique_ptr>#$@S@Foo@FI@value", + "uses": ["tests/usage/type_usage_as_template_parameter.cc:15:6"] + }] } */ \ No newline at end of file