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