diff --git a/main.cpp b/main.cpp index 7cbcb844..a63b98a0 100644 --- a/main.cpp +++ b/main.cpp @@ -18,8 +18,6 @@ //#include -// TODO: Maybe we should use clang_indexSourceFile? - // While indexing, we should refer to symbols by USR. When joining into the db, we can have optimized access. struct TypeDef; @@ -1282,18 +1280,6 @@ bool HasUsage(const std::vector& usages, const clang::Sou return false; } -// TODO: Let's switch over to the indexer api. It can index -// the int x = get_value() bit... -// - problem: prototype ParmDecl are not parsed - we can parse this info though using FuncDecl and checking if it prototype -// - make sure type hierarchy is possible - CXIdxCXXClassDeclInfo -// - make sure namespace is possible - look at container/lexicalContainer -// - make sure method overload is possible - should be doable using existing approach -// -// - make sure template logic is possible -// * it doesn't seem like we get any template specialization logic -// * we get two decls to the same template... resolved by checking parent? maybe this will break. not sure. - - bool IsTypeDefinition(const CXIdxContainerInfo* container) { if (!container) return false; @@ -1307,22 +1293,43 @@ bool IsTypeDefinition(const CXIdxContainerInfo* container) { } } -std::optional ResolveToType(ParsingDatabase* db, clang::Type type) { - clang::Type var_type = type.strip_qualifiers(); - std::string usr = var_type.get_usr(); +#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 std::nullopt; + 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. - clang::Cursor decl = clang_getTypeDeclaration(var_type.cx_type); - clang::Cursor unresolved_decl = clang_getSpecializedCursorTemplate(decl.cx_cursor); - std::string template_usr = clang::Cursor(unresolved_decl).get_usr(); - if (template_usr != "") - usr = template_usr; + 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; + } + */ + } - return db->ToTypeId(usr); + result.resolved_type = db->ToTypeId(usr); + return result; } clang::SourceLocation FindLocationOfTypeSpecifier(clang::Cursor cursor) { @@ -1334,13 +1341,126 @@ clang::SourceLocation FindLocationOfTypeSpecifier(clang::Cursor cursor) { return child.value().get_source_location(); } -void AddInterestingUsageToType(ParsingDatabase* db, TypeId type_id, clang::SourceLocation location) { - TypeDef* type_def = db->Resolve(type_id); + + +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; + bool is_interesting; + int has_processed_any = false; + std::optional previous_cursor; + std::optional initial_type; + + VisitDeclForTypeUsageParam(ParsingDatabase* db, bool is_interesting) + : db(db), is_interesting(is_interesting) {} +}; + +void VisitDeclForTypeUsageVisitorHandler(clang::Cursor cursor, VisitDeclForTypeUsageParam* param) { + param->has_processed_any = true; + ParsingDatabase* db = param->db; + + TypeId ref_type_id = db->ToTypeId(cursor.get_referenced().get_usr()); + if (!param->initial_type) + param->initial_type = ref_type_id; + + if (param->is_interesting) { + TypeDef* ref_type_def = db->Resolve(ref_type_id); + ref_type_def->interesting_uses.push_back(cursor.get_source_location()); + } } +clang::VisiterResult VisitDeclForTypeUsageVisitor(clang::Cursor cursor, clang::Cursor parent, VisitDeclForTypeUsageParam* param) { + switch (cursor.get_kind()) { + case CXCursor_TemplateRef: + case CXCursor_TypeRef: + if (param->previous_cursor) { + VisitDeclForTypeUsageVisitorHandler(param->previous_cursor.value(), param); + // This if is inside the above if because if there are multiple TypeRefs, + // we always want to process the first one. If we did not always process + // the first one, we cannot tell if there are more TypeRefs after it and + // logic for fetching the return type breaks. This happens in ParmDecl + // instances which only have one TypeRef child but are not interesting + // usages. + if (!param->is_interesting) + return clang::VisiterResult::Break; + } + param->previous_cursor = cursor; + } + + return clang::VisiterResult::Continue; +} + +std::optional ResolveDeclToType(ParsingDatabase* db, clang::Cursor decl_cursor, const CXIdxContainerInfo* semantic_container, const CXIdxContainerInfo* lexical_container, bool is_interesting) { + // + // The general AST format for definitions follows this pattern: + // + // template + // struct Container; + // + // struct S1; + // struct S2; + // + // Container, S2> foo; + // + // => + // + // VarDecl + // TemplateRef Container + // TemplateRef Container + // TypeRef struct S1 + // TypeRef struct S2 + // TypeRef struct S2 + // + + // We skip the last type reference for methods/variables which are defined + // out-of-line w.r.t. the parent type. + // + // S1* Foo::foo() {} + // + // The above example looks like this in the AST: + // + // CXXMethod foo + // TypeRef struct S1 + // TypeRef class Foo + // CompoundStmt + // ... + // + // The second TypeRef is an uninteresting usage. + bool process_last_type_ref = true; + if (IsTypeDefinition(semantic_container) && !IsTypeDefinition(lexical_container)) { + assert(decl_cursor.is_definition()); + process_last_type_ref = false; + } + + VisitDeclForTypeUsageParam param(db, is_interesting); + decl_cursor.VisitChildren(&VisitDeclForTypeUsageVisitor, ¶m); + + // VisitDeclForTypeUsageVisitor guarantees that if there are multiple TypeRef + // children, the first one will always be visited. + if (param.previous_cursor && process_last_type_ref) { + 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). + assert(!param.previous_cursor.has_value() || + param.previous_cursor.value().get_kind() == CXCursor_TypeRef); + } + + return param.initial_type; +} void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { @@ -1378,16 +1498,21 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { var_def->all_uses.push_back(decl->loc); + std::optional var_type = ResolveDeclToType(db, decl_cursor, decl->semanticContainer, decl->lexicalContainer, decl_cursor.get_kind() != CXCursor_ParmDecl); + if (var_type.has_value()) + var_def->variable_type = var_type.value(); // Declaring variable type information. - std::optional var_type_id = ResolveToType(db, decl_cursor.get_type()); - if (var_type_id) { - var_def->variable_type = var_type_id.value(); + /* + TypeResolution var_type = ResolveToType(db, decl_cursor.get_type()); + if (var_type.resolved_type) { + var_def->variable_type = var_type.resolved_type.value(); // Insert an interesting type usage for variable declarations. Parameters // are handled when a function is declared because clang doesn't provide // parameter declarations for unnamed parameters. if (decl_cursor.get_kind() != CXCursor_ParmDecl) - AddInterestingUsageToType(db, var_type_id.value(), FindLocationOfTypeSpecifier(decl_cursor)); + AddInterestingUsageToType(db, var_type, FindLocationOfTypeSpecifier(decl_cursor)); } + */ if (decl->isDefinition && IsTypeDefinition(decl->semanticContainer)) { @@ -1440,9 +1565,13 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { declaring_type_def->funcs.push_back(func_id); } - std::optional ret_type_id = ResolveToType(db, decl_cursor.get_type().get_return_type()); - if (ret_type_id) - AddInterestingUsageToType(db, ret_type_id.value(), FindLocationOfTypeSpecifier(decl_cursor)); + // We don't actually need to know the return type, but we need to mark it + // as an interesting usage. + ResolveDeclToType(db, decl_cursor, decl->semanticContainer, decl->lexicalContainer, true /*is_interesting*/); + + //TypeResolution ret_type = ResolveToType(db, decl_cursor.get_type().get_return_type()); + //if (ret_type.resolved_type) + // AddInterestingUsageToType(db, ret_type, FindLocationOfTypeSpecifier(decl_cursor)); if (decl->isDefinition || is_pure_virtual) { // Mark type usage for parameters as interesting. We handle this here @@ -1458,9 +1587,13 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { for (clang::Cursor arg : cursor.get_arguments()) { switch (arg.get_kind()) { case CXCursor_ParmDecl: - std::optional arg_type_id = ResolveToType(db, arg.get_type()); - if (arg_type_id) - AddInterestingUsageToType(db, arg_type_id.value(), FindLocationOfTypeSpecifier(arg)); + // We don't need to know the arg type, but we do want to mark it as + // an interesting usage. + ResolveDeclToType(db, arg, decl->semanticContainer, decl->lexicalContainer, true /*is_interesting*/); + + //TypeResolution arg_type = ResolveToType(db, arg.get_type()); + //if (arg_type.resolved_type) + // AddInterestingUsageToType(db, arg_type, FindLocationOfTypeSpecifier(arg)); break; } } @@ -1672,7 +1805,6 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re //if (param->last_type_usage_location == loc) break; //param->last_type_usage_location = loc; - // TODO: initializer list can many type refs... if (!HasUsage(referenced_def->all_uses, loc)) referenced_def->all_uses.push_back(loc); @@ -1862,12 +1994,14 @@ int main(int argc, char** argv) { */ for (std::string path : GetFilesInFolder("tests")) { - //if (path != "tests/declaration_vs_definition/method.cc") continue; - //if (path == "tests/usage/type_usage_declare_extern.cc") continue; + //if (path != "tests/declaration_vs_definition/class_member_static.cc") continue; + //if (path != "tests/usage/type_usage_declare_param.cc") continue; //if (path == "tests/constructors/constructor.cc") continue; //if (path == "tests/constructors/destructor.cc") continue; //if (path == "tests/usage/func_usage_call_method.cc") continue; - if (path != "tests/usage/type_usage_as_template_parameter_simple.cc") continue; + //if (path != "tests/usage/type_usage_as_template_parameter.cc") continue; + //if (path != "tests/usage/type_usage_as_template_parameter_complex.cc") continue; + //if (path != "tests/usage/type_usage_as_template_parameter_simple.cc") continue; //if (path != "tests/usage/type_usage_typedef_and_using.cc") continue; //if (path != "tests/usage/type_usage_declare_local.cc") continue; //if (path != "tests/usage/func_usage_addr_method.cc") continue; diff --git a/tests/usage/func_usage_template_func.cc b/tests/usage/func_usage_template_func.cc index 572d6d3f..dc0ddf0b 100644 --- a/tests/usage/func_usage_template_func.cc +++ b/tests/usage/func_usage_template_func.cc @@ -1,5 +1,5 @@ template -void accept(T) {} +void accept(T); void foo() { accept(1); @@ -15,7 +15,7 @@ OUTPUT: "usr": "c:@FT@>1#Taccept#t0.0#v#", "short_name": "accept", "qualified_name": "accept", - "definition": "tests/usage/func_usage_template_func.cc:2:6", + "declaration": "tests/usage/func_usage_template_func.cc:2:6", "callers": ["1@tests/usage/func_usage_template_func.cc:5:3", "1@tests/usage/func_usage_template_func.cc:6:3"], "all_uses": ["tests/usage/func_usage_template_func.cc:2:6", "tests/usage/func_usage_template_func.cc:5:3", "tests/usage/func_usage_template_func.cc:6:3"] }, { diff --git a/tests/usage/type_usage_as_template_parameter.cc b/tests/usage/type_usage_as_template_parameter.cc index dc0a6354..be8d0cb5 100644 --- a/tests/usage/type_usage_as_template_parameter.cc +++ b/tests/usage/type_usage_as_template_parameter.cc @@ -24,7 +24,8 @@ OUTPUT: "short_name": "S", "qualified_name": "S", "definition": "tests/usage/type_usage_as_template_parameter.cc:4:8", - "all_uses": ["tests/usage/type_usage_as_template_parameter.cc:4:8", "tests/usage/type_usage_as_template_parameter.cc:7:19", "tests/usage/type_usage_as_template_parameter.cc:9:12", "tests/usage/type_usage_as_template_parameter.cc:10:14"] + "all_uses": ["tests/usage/type_usage_as_template_parameter.cc:4:8", "tests/usage/type_usage_as_template_parameter.cc:7:19", "tests/usage/type_usage_as_template_parameter.cc:9:12", "tests/usage/type_usage_as_template_parameter.cc:10:14"], + "interesting_uses": ["tests/usage/type_usage_as_template_parameter.cc:7:19", "tests/usage/type_usage_as_template_parameter.cc:9:12", "tests/usage/type_usage_as_template_parameter.cc:10:14"] }], "functions": [{ "id": 0, diff --git a/tests/usage/type_usage_as_template_parameter_complex.cc b/tests/usage/type_usage_as_template_parameter_complex.cc new file mode 100644 index 00000000..de48288a --- /dev/null +++ b/tests/usage/type_usage_as_template_parameter_complex.cc @@ -0,0 +1,157 @@ +template +class unique_ptr; + +struct S1; +struct S2; + +#if false +VarDecl f + TemplateRef unique_ptr + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + TypeRef struct S2 +#endif +extern unique_ptr, S2> f; + +#if false +FunctionDecl as_return_type + TemplateRef unique_ptr + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + TypeRef struct S2 + ParmDecl + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + CompoundStmt + ReturnStmt + UnexposedExpr + CXXNullPtrLiteralExpr +#endif +unique_ptr, S2>* as_return_type(unique_ptr*) { return nullptr; } + +#if false +FunctionDecl no_return_type + ParmDecl + CompoundStmt +#endif +void no_return_type(int) {} + +#if false +FunctionDecl empty + CompoundStmt + DeclStmt + VarDecl local + TemplateRef unique_ptr + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + TypeRef struct S2 +#endif +void empty() { + unique_ptr, S2>* local; +} + +#if false +ClassDecl Foo + CXXMethod foo + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 +#endif +class Foo { + unique_ptr* foo(); +}; + +#if false +CXXMethod foo + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + TypeRef class Foo + CompoundStmt + ReturnStmt + UnexposedExpr + CXXNullPtrLiteralExpr +#endif +unique_ptr* Foo::foo() { return nullptr; } + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@ST>2#T#T@unique_ptr", + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:2:7", "tests/usage/type_usage_as_template_parameter_complex.cc:15:8", "tests/usage/type_usage_as_template_parameter_complex.cc:15:19", "tests/usage/type_usage_as_template_parameter_complex.cc:33:1", "tests/usage/type_usage_as_template_parameter_complex.cc:33:12", "tests/usage/type_usage_as_template_parameter_complex.cc:33:52", "tests/usage/type_usage_as_template_parameter_complex.cc:54:3", "tests/usage/type_usage_as_template_parameter_complex.cc:54:14", "tests/usage/type_usage_as_template_parameter_complex.cc:65:3", "tests/usage/type_usage_as_template_parameter_complex.cc:79:1"], + "interesting_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:15:8", "tests/usage/type_usage_as_template_parameter_complex.cc:15:19", "tests/usage/type_usage_as_template_parameter_complex.cc:33:1", "tests/usage/type_usage_as_template_parameter_complex.cc:33:12", "tests/usage/type_usage_as_template_parameter_complex.cc:33:52", "tests/usage/type_usage_as_template_parameter_complex.cc:54:3", "tests/usage/type_usage_as_template_parameter_complex.cc:54:14", "tests/usage/type_usage_as_template_parameter_complex.cc:65:3", "tests/usage/type_usage_as_template_parameter_complex.cc:79:1"] + }, { + "id": 1, + "usr": "c:@S@S1", + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:4:8", "tests/usage/type_usage_as_template_parameter_complex.cc:15:30", "tests/usage/type_usage_as_template_parameter_complex.cc:33:23", "tests/usage/type_usage_as_template_parameter_complex.cc:33:63", "tests/usage/type_usage_as_template_parameter_complex.cc:54:25", "tests/usage/type_usage_as_template_parameter_complex.cc:65:14", "tests/usage/type_usage_as_template_parameter_complex.cc:79:12"], + "interesting_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:15:30", "tests/usage/type_usage_as_template_parameter_complex.cc:33:23", "tests/usage/type_usage_as_template_parameter_complex.cc:33:63", "tests/usage/type_usage_as_template_parameter_complex.cc:54:25", "tests/usage/type_usage_as_template_parameter_complex.cc:65:14", "tests/usage/type_usage_as_template_parameter_complex.cc:79:12"] + }, { + "id": 2, + "usr": "c:@S@S2", + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:5:8", "tests/usage/type_usage_as_template_parameter_complex.cc:15:34", "tests/usage/type_usage_as_template_parameter_complex.cc:15:39", "tests/usage/type_usage_as_template_parameter_complex.cc:33:27", "tests/usage/type_usage_as_template_parameter_complex.cc:33:32", "tests/usage/type_usage_as_template_parameter_complex.cc:33:67", "tests/usage/type_usage_as_template_parameter_complex.cc:54:29", "tests/usage/type_usage_as_template_parameter_complex.cc:54:34", "tests/usage/type_usage_as_template_parameter_complex.cc:65:18", "tests/usage/type_usage_as_template_parameter_complex.cc:79:16"], + "interesting_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:15:34", "tests/usage/type_usage_as_template_parameter_complex.cc:15:39", "tests/usage/type_usage_as_template_parameter_complex.cc:33:27", "tests/usage/type_usage_as_template_parameter_complex.cc:33:32", "tests/usage/type_usage_as_template_parameter_complex.cc:33:67", "tests/usage/type_usage_as_template_parameter_complex.cc:54:29", "tests/usage/type_usage_as_template_parameter_complex.cc:54:34", "tests/usage/type_usage_as_template_parameter_complex.cc:65:18", "tests/usage/type_usage_as_template_parameter_complex.cc:79:16"] + }, { + "id": 3, + "usr": "c:@S@Foo", + "short_name": "Foo", + "qualified_name": "Foo", + "definition": "tests/usage/type_usage_as_template_parameter_complex.cc:64:7", + "funcs": [3], + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:64:7", "tests/usage/type_usage_as_template_parameter_complex.cc:79:21"] + }], + "functions": [{ + "id": 0, + "usr": "c:@F@as_return_type#*$@S@unique_ptr>#$@S@S1#$@S@S2#", + "short_name": "as_return_type", + "qualified_name": "as_return_type", + "definition": "tests/usage/type_usage_as_template_parameter_complex.cc:33:37", + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:33:37"] + }, { + "id": 1, + "usr": "c:@F@no_return_type#I#", + "short_name": "no_return_type", + "qualified_name": "no_return_type", + "definition": "tests/usage/type_usage_as_template_parameter_complex.cc:40:6", + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:40:6"] + }, { + "id": 2, + "usr": "c:@F@empty#", + "short_name": "empty", + "qualified_name": "empty", + "definition": "tests/usage/type_usage_as_template_parameter_complex.cc:53:6", + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:53:6"] + }, { + "id": 3, + "usr": "c:@S@Foo@F@foo#", + "short_name": "foo", + "qualified_name": "Foo::foo", + "declaration": "tests/usage/type_usage_as_template_parameter_complex.cc:65:23", + "definition": "tests/usage/type_usage_as_template_parameter_complex.cc:79:26", + "declaring_type": 3, + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:65:23", "tests/usage/type_usage_as_template_parameter_complex.cc:79:26"] + }], + "variables": [{ + "id": 0, + "usr": "c:@f", + "short_name": "f", + "qualified_name": "f", + "declaration": "tests/usage/type_usage_as_template_parameter_complex.cc:15:43", + "variable_type": 0, + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:15:43"] + }, { + "id": 1, + "usr": "c:type_usage_as_template_parameter_complex.cc@1062@F@empty#@local", + "short_name": "local", + "qualified_name": "local", + "definition": "tests/usage/type_usage_as_template_parameter_complex.cc:54:39", + "variable_type": 0, + "all_uses": ["tests/usage/type_usage_as_template_parameter_complex.cc:54:39"] + }] +} +*/ \ No newline at end of file diff --git a/tests/usage/type_usage_as_template_parameter_simple.cc b/tests/usage/type_usage_as_template_parameter_simple.cc index e6d90b11..89e7bf3e 100644 --- a/tests/usage/type_usage_as_template_parameter_simple.cc +++ b/tests/usage/type_usage_as_template_parameter_simple.cc @@ -1,34 +1,36 @@ template -class unique_ptr; +class unique_ptr {}; struct S; -extern unique_ptr f; +static unique_ptr foo; /* -// TODO: There should be an interesting usage on S as well. OUTPUT: { "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_simple.cc:2:7", "all_uses": ["tests/usage/type_usage_as_template_parameter_simple.cc:2:7", "tests/usage/type_usage_as_template_parameter_simple.cc:6:8"], "interesting_uses": ["tests/usage/type_usage_as_template_parameter_simple.cc:6:8"] }, { "id": 1, "usr": "c:@S@S", - "all_uses": ["tests/usage/type_usage_as_template_parameter_simple.cc:4:8", "tests/usage/type_usage_as_template_parameter_simple.cc:6:19"] + "all_uses": ["tests/usage/type_usage_as_template_parameter_simple.cc:4:8", "tests/usage/type_usage_as_template_parameter_simple.cc:6:19"], + "interesting_uses": ["tests/usage/type_usage_as_template_parameter_simple.cc:6:19"] }], "functions": [], "variables": [{ "id": 0, - "usr": "c:@f", - "short_name": "f", - "qualified_name": "f", - "declaration": "tests/usage/type_usage_as_template_parameter_simple.cc:6:22", + "usr": "c:type_usage_as_template_parameter_simple.cc@foo", + "short_name": "foo", + "qualified_name": "foo", + "definition": "tests/usage/type_usage_as_template_parameter_simple.cc:6:22", "variable_type": 0, "all_uses": ["tests/usage/type_usage_as_template_parameter_simple.cc:6:22"] }] } - */ \ No newline at end of file