[cfe-dev] Using libclang to inspect the AST of template arguments

Manuel Klimek klimek at google.com
Sun May 25 23:25:42 PDT 2014


On Sun, May 25, 2014 at 5:53 PM, Allan Nielsen <a at awn.dk> wrote:

> 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 :)

Cheers,
/Manuel


>
> Any hints or guidance is most appreciated.
>
> /Allan
>
> PS: Following are the code I wrote for this experiments:
>
> #include <map>
> #include <stdio.h>
> #include <iostream>
> #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;
> }
>
>
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20140526/21f8b4d8/attachment.html>


More information about the cfe-dev mailing list