Using libclang to inspect the AST of template arguments

Hi,

I’m trying to write a program which uses libclang to refacture some code. To do
this I need to inspect the AST of the template arguments, but I can not figure
out how I can build a tree from the vistor callbacks.

Here is what my test source code looks like:

1: template<typename …T>
2: struct Foo {};
3:
4: static Foo<int, int, Foo<short, long>> foo;

When running the clang_visitChildren on this code I receive the following
callbacks:

(SYNTAX: / : )

1/0 /tmp/test.cxx:1,1-2,14 31 Foo
0/2 /tmp/test.cxx:1,10-23 27 T type-parameter-0-0
1/3 /tmp/test.cxx:4,1-43 9 foo Foo<int, int, Foo<short, long> >
3/4 /tmp/test.cxx:4,8-11 45 Foo
3/4 /tmp/test.cxx:4,22-25 45 Foo
3/5 /tmp/test.cxx:4,40-43 103 Foo Foo<int, int, Foo<short, long> >

What confuses me is that all callbacks on line 4 has the same parent… So how
can I build a tree of the template arguments?

Should I be using the c++ API instead of the C-API for this?

Any hints or guidance is most appreciated.

/Allan

PS: Following are the code I wrote for this experiments:

#include
#include <stdio.h>
#include
#include “clang-c/Index.h”

static std::map<unsigned, size_t> hashes;

size_t hash(const CXCursor &c) {
unsigned h = clang_hashCursor(c);
if (hashes.find(h) == hashes.end()) {
size_t s = hashes.size();
hashes[h] = s;
return s;
} else {
return hashes[h];
}
}

struct WString {
WString(CXString s) : str(s) {}
~WString() { clang_disposeString(str); }
CXString str;
};

std::ostream &operator<<(std::ostream &o, const CXString &s) {
if (clang_getCString(s)) o << clang_getCString(s);
return o;
}

std::ostream &operator<<(std::ostream &o, const CXSourceRange &range) {
CXSourceLocation start = clang_getRangeStart(range);
CXSourceLocation end = clang_getRangeEnd(range);

CXFile file;
unsigned start_line = 0;
unsigned start_col = 0;
unsigned end_line = 0;
unsigned end_col = 0;

clang_getExpansionLocation(start, &file, &start_line, &start_col, 0);
clang_getExpansionLocation(end, 0, &end_line, &end_col, 0);

o << clang_getFileName(file) << “:”;
if (start_line == end_line)
o << start_line << “,” << start_col << “-” << end_col;
else
o << start_line << “,” << start_col << “-” << end_line << “,”
<< end_col;

return o;
}

enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
CXClientData client_data) {
if (cursor.kind == CXCursor_MacroDefinition) return CXChildVisit_Recurse;

CXSourceRange range = clang_getCursorExtent(cursor);
CXType t = clang_getCursorType(cursor);
CXType tt = clang_getCanonicalType(t);

std::cout << hash(parent) << “/” << hash(cursor) << " " << range << " "
<< cursor.kind << " " << clang_getCursorDisplayName(cursor) << " "
<< clang_getTypeSpelling(tt) << std::endl;

return CXChildVisit_Recurse;
}

int main(int argc, char *argv[]) {
CXIndex idx = clang_createIndex(1, 1);
CXTranslationUnit tu = clang_createTranslationUnitFromSourceFile(
idx, 0, argc - 1, argv + 1, 0, 0);
clang_visitChildren(clang_getTranslationUnitCursor(tu), visit, 0);
clang_disposeTranslationUnit(tu);
return 0;
}

Hi,

I'm trying to write a program which uses libclang to refacture some code.
To do
this I need to inspect the AST of the template arguments, but I can not
figure
out how I can build a tree from the vistor callbacks.

Here is what my test source code looks like:

    1: template<typename ...T>
    2: struct Foo {};
    3:
    4: static Foo<int, int, Foo<short, long>> foo;

When running the clang_visitChildren on this code I receive the following
callbacks:

    (SYNTAX: <PARENT-CURSOR-HASH>/<CURSOR-HASH> <FILE>:<RANGE> <KIND>
<DISPLAYNAME> <DISPLAYTYPE>)

    1/0 /tmp/test.cxx:1,1-2,14 31 Foo<T>
    0/2 /tmp/test.cxx:1,10-23 27 T type-parameter-0-0
    1/3 /tmp/test.cxx:4,1-43 9 foo Foo<int, int, Foo<short, long> >
    3/4 /tmp/test.cxx:4,8-11 45 Foo
    3/4 /tmp/test.cxx:4,22-25 45 Foo
    3/5 /tmp/test.cxx:4,40-43 103 Foo Foo<int, int, Foo<short, long> >

What confuses me is that all callbacks on line 4 has the same parent... So
how
can I build a tree of the template arguments?

Should I be using the c++ API instead of the C-API for this?

Front-loading by saying that I'm not an expert on the C-API:
1. You want to use the C-API if you need a library that is stable, and
provides for backwards compatibility going forward. That is, if you want to
write this tool once, and than use it for years without ever having to
touch it, the C API is your only hope.
2. As you discovered, the C API makes some power uses harder; I don't know
whether what you want to do is possible with the C API, but I'd say that
it's quite a bit easier with the C++ API; that said, if you use the C++
API, you'll have to recompile your tool when you want to use a newer clang,
and there are sometimes breaking interface changes, for which you'll have
to adapt your code (that said, those interface changes are not a big issue
- we have many tools against the C++ API we maintain, and have not found
that to be an issue - the "being willing to recompile" is the much larger
barrier-to-entry, I think).

So, I'd cautiously advice you to use the C++ API if you're coding in C++
anyway, and you don't want to ship a tool that can auto-link against
different versions of clang to customers.

Others might jump in with different opinions :slight_smile:

Cheers,
/Manuel

Hi,

Thanks for the advice, I will look into using the C++-API instead. The only
reason why I have chosen the C-API was that it seem easier to get started
with...

Best regards
Allan W. Nielsen