better template indexing

This commit is contained in:
Jacob Dufault 2017-03-05 14:49:23 -08:00
parent d403d7ad96
commit 8e36152406
19 changed files with 841 additions and 52 deletions

View File

@ -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.

View File

@ -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<TypeId> 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<A,B> => Foo<*,*>).
optional<TypeId> 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<TypeId> AddDeclUsages(IndexedFile* db, clang::Cursor decl_cursor,
bool is_interesting, const CXIdxContainerInfo* semantic_container,
const CXIdxContainerInfo* lexical_container) {
//
@ -415,7 +428,46 @@ optional<TypeId> ResolveDeclToType(IndexedFile* db, clang::Cursor decl_cursor,
// TypeRef struct S2
// TypeRef struct S2
//
//
// Here is another example:
//
// enum A {};
// enum B {};
//
// template<typename T>
// struct Foo {
// struct Inner {};
// };
//
// Foo<A>::Inner a;
// Foo<B> b;
//
// =>
//
// EnumDecl A
// EnumDecl B
// ClassTemplate Foo
// TemplateTypeParameter T
// StructDecl Inner
// VarDecl a
// TemplateRef Foo
// TypeRef enum A
// TypeRef struct Foo<enum A>::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<TypeId> 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<TypeId> 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<TypeId> 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<TypeId> 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<TypeId> 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<TypeId> 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<TypeId> 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<std::string> args, bool dump
NamespaceHelper ns;
IndexParam param(&db, &ns);
clang_indexTranslationUnit(index_action, &param, callbacks, sizeof(callbacks),
CXIndexOpt_IndexFunctionLocalSymbols | CXIndexOpt_SkipParsedBodiesInSession, tu.cx_tu);
CXIndexOpt_IndexFunctionLocalSymbols | CXIndexOpt_SkipParsedBodiesInSession | CXIndexOpt_IndexImplicitTemplateInstantiations, tu.cx_tu);
clang_IndexAction_dispose(index_action);

View File

@ -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 {

View File

@ -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;

12
test.cc
View File

@ -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();

View File

@ -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"]
}]

View File

@ -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"]
}]

View File

@ -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"]
}]

View File

@ -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"]
}, {

View File

@ -1,40 +1,267 @@
void called(int a);
enum A {};
enum B {};
int gen() { return 1; }
template<typename T>
struct Foo {
struct Inner {};
};
Foo<A>::Inner a;
Foo<B> 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<enum A>::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"]
}]
}
*/
*/
#if false
namespace ns {
enum VarType {};
template<typename _>
struct Holder {
static constexpr VarType static_var = (VarType)0x0;
};
template<typename _>
const typename VarType Holder<_>::static_var;
int Foo = Holder<int>::static_var;
int Foo2 = Holder<int>::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 <string>
//#include <xiosbase>
//#include <sstream>
//#include <algorithm>
//#include <vector>
//#include <string>
//#include <cstddef>
//#include <sstream>
//#include <iomanip>
//#include <limits>
//#include <vector>
//#include <cstddef>
//#include <tuple>
//#include <type_traits>
//#include <string>
//#include <string>
//#include <type_traits>
//#include <iterator>
//#include <vector>
//#include <string>
//#include <stdlib.h>
//#include <string>
//#include <vector>
//#include <string>
//#include <cstddef>
//#include <cmath>
//#include <limits>
//#include <type_traits>
//#include <set>
//#include <string>
//#include <vector>
//#include <iosfwd>
//#include <streambuf>
//#include <ostream>
//#include <fstream>
//#include <memory>
//#include <vector>
//#include <string>
//#include <stdexcept>
//#include <string>
//#include <vector>
//#include <sstream>
//#include <algorithm>

View File

@ -0,0 +1,51 @@
namespace ns {
template<typename T>
struct Foo {
template<typename R>
static int foo() {
return 3;
}
};
int a = Foo<int>::foo<float>();
int b = Foo<bool>::foo<double>();
}
/*
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"]
}]
}
*/

View File

@ -0,0 +1,39 @@
namespace ns {
template<typename T>
class Foo {};
Foo<int> a;
Foo<bool> 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"]
}]
}
*/

View File

@ -0,0 +1,48 @@
template<typename T>
struct Foo {
static int foo() {
return 3;
}
};
int a = Foo<int>::foo();
int b = Foo<bool>::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"]
}]
}
*/

View File

@ -0,0 +1,49 @@
template<typename T>
struct Foo {
template<typename R>
static int foo() {
return 3;
}
};
int a = Foo<int>::foo<float>();
int b = Foo<bool>::foo<double>();
/*
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"]
}]
}
*/

View File

@ -0,0 +1,81 @@
enum A {};
enum B {};
template<typename T>
struct Foo {
struct Inner {};
};
Foo<A>::Inner a;
Foo<B>::Inner b;
#if false
EnumDecl A
EnumDecl B
ClassTemplate Foo
TemplateTypeParameter T
StructDecl Inner
VarDecl a
TemplateRef Foo
TypeRef enum A
TypeRef struct Foo<enum A>::Inner
CallExpr Inner
VarDecl b
TemplateRef Foo
TypeRef enum B
TypeRef struct Foo<enum B>::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"]
}]
}
*/

View File

@ -0,0 +1,44 @@
template<typename T>
struct Foo {
static constexpr int var = 3;
};
int a = Foo<int>::var;
int b = Foo<bool>::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"]
}]
}
*/

View File

@ -0,0 +1,40 @@
template<typename T>
static int foo() {
return 3;
}
int a = foo<int>();
int b = foo<bool>();
// 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"]
}]
}
*/

View File

@ -0,0 +1,37 @@
template<typename T>
class Foo {};
Foo<int> a;
Foo<bool> 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"]
}]
}
*/

View File

@ -0,0 +1,73 @@
enum A {};
enum B {};
template<typename T>
T var = 3;
int a = var<A>;
int b = var<B>;
// 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"]
}]
}
*/