Search code examples
pythonc++cclanglibclang

Finding anonymous enums with libclang


Is there a way to detect anonymous enumerations using libclang without relying on the text in the spelling name?

The python bindings to libclang include functionality to detect whether C/C++ structs or unions are anonymous using clang.cindex.Cursor.is_anonymous, which ends up calling clang_Cursor_isAnonymous.

The following sample demonstrates the issue.

import sys
from clang.cindex import *

def nodeinfo(n):
    return (n.kind, n.is_anonymous(), n.spelling, n.type.spelling)

idx = Index.create()

# translation unit parsed correctly
tu = idx.parse(sys.argv[1], ['-std=c++11'])
assert(len(tu.diagnostics) == 0)

for n in tu.cursor.walk_preorder():
    if n.kind == CursorKind.STRUCT_DECL and n.is_anonymous():
        print nodeinfo(n)
    if n.kind == CursorKind.UNION_DECL and n.is_anonymous():
        print nodeinfo(n)
    if n.kind == CursorKind.ENUM_DECL:
        if n.is_anonymous():
            print nodeinfo(n)
        else:
            print 'INCORRECT', nodeinfo(n)

Which when run on sample.cpp

enum
{
    VAL = 1
};

struct s
{
    struct {};
    union
    {
        int x;
        float y;
    };
};

Gives:

INCORRECT (CursorKind.ENUM_DECL, False, '', '(anonymous enum at sample1.cpp:1:1)')
(CursorKind.STRUCT_DECL, True, '', 's::(anonymous struct at sample1.cpp:8:5)')
(CursorKind.UNION_DECL, True, '', 's::(anonymous union at sample1.cpp:9:5)')

Solution

  • Unfortunately clang_Cursor_isAnonymous works only with structs and unions as you can see from clang source code in tools/libclang/CXType.cpp

    unsigned clang_Cursor_isAnonymous(CXCursor C){
      if (!clang_isDeclaration(C.kind))
        return 0;
      const Decl *D = cxcursor::getCursorDecl(C);
      if (const RecordDecl *FD = dyn_cast_or_null<RecordDecl>(D))
        return FD->isAnonymousStructOrUnion();
      return 0;
    }
    

    So fallback to the conf.lib.clang_Cursor_isAnonymous in clang.cindex.Cursor.is_anonymous does nothing new as cursor type has been already checked against FIELD_DECL (which is true only for structs and unions)

    def is_anonymous(self):
            """
            Check if the record is anonymous.
            """
            if self.kind == CursorKind.FIELD_DECL:
                return self.type.get_declaration().is_anonymous()
            return conf.lib.clang_Cursor_isAnonymous(self)
    

    You can try to extract identifier of current element (n in your sample) and check if it exists or is null