pythonc++parsingabstract-syntax-treelibclang

Using Libclang python bindings, how do you retrieve annotations added to C++ class/struct?


Using the following C++ struct as an example:

__attribute__((annotate("MyAttribute")))
struct TestComponent
{
    __attribute__((annotate("MyAttribute")))
    int32_t testInt;
    
    __attribute__((annotate("MyAttribute")))
    bool testBool;

    __attribute__((annotate("MyAttribute")))
    char testChar;
};

Given a node (cursor) from clang using (clang module's cindex) while parsing the AST, using the following I can get the annotations on the class members when node is pointing to TestComponent and has a kind equal to CursorKind.STRUCT_DECL:

def get_annotations(node):
    annotations = [c.displayname for c in node.get_children()
            if c.kind == clang.cindex.CursorKind.ANNOTATE_ATTR]
    return annotations

But class/struct annotations never show up as children of a struct/class. Is there a way to get to those? Even in the C bindings - I can try Monkeypatching it, but couldn't find anything.


Solution

  • tl;dr: attribute should be placed between struct and TestComponent

    struct __attribute__((annotate("MyAttribute"))) TestComponent
    

    If you print tu.diagnostics, clang does issue a -Wignored-attributes warning:

    test.cpp:2:17: warning: attribute 'annotate' is ignored, place it after "struct" to apply attribute to type declaration [-Wignored-attributes]
    

    Once fixed, ANNOTATE_ATTR is visible in AST:

     test.cpp CursorKind.TRANSLATION_UNIT
       TestComponent CursorKind.STRUCT_DECL
         MyStruct CursorKind.ANNOTATE_ATTR
         testInt CursorKind.FIELD_DECL
           MyInt CursorKind.ANNOTATE_ATTR
         testBool CursorKind.FIELD_DECL
           MyBool CursorKind.ANNOTATE_ATTR
         testChar CursorKind.FIELD_DECL
           MyChar CursorKind.ANNOTATE_ATTR
    
    

    My test code for reference:

    code = """
    __attribute__((annotate("InvalidAttribute"))) 
    struct __attribute__((annotate("MyStruct"))) TestComponent
    {
        __attribute__((annotate("MyInt")))
        int testInt;
    
        __attribute__((annotate("MyBool")))
        bool testBool;
    
        __attribute__((annotate("MyChar")))
        char testChar;
    };
    """
    
    from clang.cindex import Cursor, Index, Config
    Config.set_library_file('C:/llvm-16/bin/libclang.dll')
    
    index = Index.create()
    args = ['-x', 'c++', '-std=c++20', 'test.cpp']
    
    tu = index.parse(None, args,
                    unsaved_files=[('test.cpp', code)])
    
    
    for d in tu.diagnostics:
        print(d)
    
    def recurse(c: Cursor, indent=0):
        print(' ' * indent, c.spelling, c.kind)
        for n in c.get_children():
            recurse(n, indent + 2)
    
    recurse(tu.cursor)