Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inspecting generalized attributes with libclang

I would like to parse generalized attributes of class member functions in the following example:

class Foo
{
public:
    void foo [[interesting]] ();
    void bar ();
};

Using the libclang C API, I would like to distinguish between foo and bar (and know that foo has the interesting attribute) in the source. Is this possible? I have a hard time finding examples or documentation that explains the concepts used in the API (I've found a reference, but that's kind of hard to use when the concepts are not explained).

like image 783
Tamás Szelei Avatar asked Oct 29 '13 09:10

Tamás Szelei


2 Answers

While I was unable to find the generalized attributes in the AST (it seems they are dropped when or before constructing the AST, not after it), I did find a workaround.

There is an annotate clang attribute in the following form:

__attribute__((annotate("something")))

With a macro I could get a reasonable syntax and an annotation which is visible in the AST:

#define INTERESTING __attribute__((annotate("interesting")))

class Foo
{
public:
    INTERESTING void foo();
    void bar();
};

The attribute will be the child of the method node, with its display_name being the annotation string. A possible AST dump:

 <CursorKind.TRANSLATION_UNIT>
  "test.h"
{
  __builtin_va_list <CursorKind.TYPEDEF_DECL>
    "__builtin_va_list"
  type_info <CursorKind.CLASS_DECL>
    "type_info"
  Foo <CursorKind.CLASS_DECL>
    "Foo"
  {
     <CursorKind.CXX_ACCESS_SPEC_DECL>
      ""
    foo <CursorKind.CXX_METHOD>
      "foo()"
    {
       <CursorKind.ANNOTATE_ATTR>
        "interesting"
    }
    bar <CursorKind.CXX_METHOD>
      "bar()"
  }
}

It produces the same output with void foo INTERESTING (); too.

like image 170
Tamás Szelei Avatar answered Oct 21 '22 23:10

Tamás Szelei


Something like the following first_attr function will fetch the cursor of the first attribute of the passed cursor if it exists, or a null cursor if it doesn't (untested code... caveat lector)

CXChildVisitResult attr_visit(CXCursor cursor, CXCursor parent, CXClientData data) {
    if (clang_isAttribute(cursor)) {
        *data = cursor;
        return CXChildVisit_Break;
    }
    return CXChildVisit_Continue;
}

CXCursor first_attr(const CXCursor& c) {
    CXCursor attr;
    unsigned visit_result = clang_visitChildren(c, attr_visit, &attr);
    if (!visit_result) // attribute not found
        attr = clang_getNullCursor();
    return attr;
}

As for finding which specific attribute a cursor a represents, the result of clang_getCursorKind(a) can help, but the only attributes exposed are:

CXCursor_IBActionAttr
CXCursor_IBOutletAttr
CXCursor_IBOutletCollectionAttr
CXCursor_CXXFinalAttr
CXCursor_CXXOverrideAttr
CXCursor_AnnotateAttr
CXCursor_AsmLabelAttr

Everything else will be a CXCursor_UnexposedAttr and the only way I can think of to get the text of it is to examine clang_getCursorExtent(a) (i.e., read the source code; cf. clang_tokenize). In the case of annotations, the specific annotation used is available through clang_getCursorDisplayName.

like image 2
Jan Ladislav Dussek Avatar answered Oct 21 '22 23:10

Jan Ladislav Dussek