Question

When using libclang, how to exclude function from stdio.h ?

When I use the below source to collect only function definitions, then I end up getting all the functions from stdio.h as well.

I read we can pass '-x c-header' kind of arguments while creating index. But does this way of giving arguments applicable in libclang.

tu = index.parse(self.filename, "-x c-header")

After including 'c-header' argument, it wants me to fill in 'unsaved_files' array as well, as per the definition of 'parse' function in 'cindex.py'.

def parse(self, path, args = [], unsaved_files = [], options = 0):

I don't know what is the right way to do this.

def funcdefn_visitor(self, node, parent, userdata):
    if node.kind == clang.cindex.CursorKind.FUNCTION_DECL: #gives function definitions
        self.func_defn.append(clang.cindex.Cursor_displayname(node))
        self.func_defn_line_no.append(node.location.line)
        self.func_defn_col_no.append(node.location.column)
    print 'Found %s [line=%s, col=%s]' % (
        clang.cindex.Cursor_displayname(node),
        node.location.line,
        node.location.column)
    return 2 # means continue visiting recursively

index = clang.cindex.Index.create()

tu = index.parse(self.filename)
#-- link cursor visitor to call back to give function definitions
clang.cindex.Cursor_visit(
    tu.cursor,
    clang.cindex.Cursor_visit_callback(self.funcdefn_visitor),
    None)
Was it helpful?

Solution

The -x c-header command-line switch is used to generate precompiled headers, not to exclude headers from the translation unit.

I think the correct way to exclude functions from a specific file would be to skip all nodes located in it while visiting the AST. To elaborate on your example, the idea is doing a first test in the visitor to skip the file as early as possible and avoid visiting all its subnodes.

def funcdefn_visitor(self, node, parent, userdata):

    # You might want to change the test here
    if node.location.file.endswith("/stdio.h"):
        print "Skipping 'stdio.h'"
        # Continue with next sibling
        return 1

    if node.kind == clang.cindex.CursorKind.FUNCTION_DECL: #gives function definitions
        self.func_defn.append(clang.cindex.Cursor_displayname(node))
        self.func_defn_line_no.append(node.location.line)
        self.func_defn_col_no.append(node.location.column)

    print 'Found %s [line=%s, col=%s]' % (
        clang.cindex.Cursor_displayname(node),
        node.location.line,
        node.location.column)

    # Continue visiting recursively
    return 2

index = clang.cindex.Index.create()

tu = index.parse(self.filename)
#-- link cursor visitor to call back to give function definitions
clang.cindex.Cursor_visit(
    tu.cursor,
    clang.cindex.Cursor_visit_callback(self.funcdefn_visitor),
    None)

Now I'm not an expert of cindex.py (libclang's python API), but I think your example follows the C API concepts rather than the python ones. Quoting from the documentation (emphasis mine):

This module provides an interface to the Clang indexing library. It is a low-level interface to the indexing library which attempts to match the Clang API directly while also being "pythonic". Notable differences from the C API are:

  • string results are returned as Python strings, not CXString objects.

  • null cursors are translated to None.

  • access to child cursors is done via iteration, not visitation.

Although cindex.py binds Cursor_visit to clang_visitChildren, it doesn't even export the CXChildVisitResult enum, meaning that you need to hardcode values for Break, Continue and Recurse. The pythonic way of doing things consists in iterating on children nodes, as returned by the Cursor.get_children() method. An example is given in these SO answers (1, 2), which you can adapt to filter out nodes based on the source file.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top