在libclang(Python)

时间:2016-05-12 18:24:38

标签: python c++ clang libclang

在Python中通过libclang解析C ++源文件时,我试图查找(行和列位置)特定函数声明的所有引用

例如:

#include <iostream>
using namespace std;

int addition (int a, int b)
{
  int r;
  r=a+b;
  return r;
}

int main ()
{
  int z, q;
  z = addition (5,3);
  q = addition (5,5);
  cout << "The first result is " << z;
  cout << "The second result is " << q;
}

因此,对于上面的源文件,我想要第5行中addition的函数声明,我希望find_all_function_decl_references(见下文)返回{{1}的引用在第15和16行。

我试过这个(改编自here

addition

另一种方法可能是存储列表中找到的所有函数声明,并在每个函数上运行import clang.cindex import ccsyspath index = clang.cindex.Index.create() translation_unit = index.parse(filename, args=args) for node in translation_unit.cursor.walk_preorder(): node_definition = node.get_definition() if node.location.file is None: continue if node.location.file.name != sourcefile: continue if node_def is None: pass if node.kind.name == 'FUNCTION_DECL': if node.kind.is_reference(): find_all_function_decl_references(node_definition.displayname) # TODO 方法。

有没有人知道如何处理这个问题?这个find_all_function_decl_references方法怎么样? (我对find_all_function_decl_references和Python很新。)

我看到this libclang找到了某种类型的所有引用,但我不确定如何根据我的需要实现它。

理想情况下,我希望能够获取任何声明的所有引用;不仅是函数,还有变量声明,参数声明(例如,第7行上例中的def find_typerefsa),类声明等。

修改Andrew's评论之后,以下是有关我的设置规范的一些详细信息:

  • LLVM 3.8.0-win64
  • libclang-py3 3.8.1
  • Python3.5.1(在Windows中,我假设是CPython)
  • 对于b,我尝试了答案here中建议的内容和another中的内容。

*请注意,鉴于我的编程经验较少,我可以通过对其工作原理的简要解释来理解答案。

1 个答案:

答案 0 :(得分:6)

真正使这个问题具有挑战性的是C ++的复杂性。

考虑C ++中可调用的内容:函数,lambdas,函数调用操作符,成员函数,模板函数和成员模板函数。因此,在仅匹配调用表达式的情况下,您需要能够消除这些情况的歧义。

此外,libclang并没有提供clang AST的完美视图(某些节点不会完全暴露,特别是一些与模板相关的节点)。因此,任意代码片段都可能(甚至可能)包含一些构造,其中AST的libclangs视图不足以将调用表达式与声明相关联。

但是,如果您准备将自己限制在该语言的一个子集中,则可能会取得一些进展 - 例如,以下示例尝试将呼叫站点与函数声明相关联。它通过使用调用表达式对AST匹配函数声明中的所有节点进行单次传递来实现此目的。

from clang.cindex import *

def is_function_call(funcdecl, c):
    """ Determine where a call-expression cursor refers to a particular function declaration
    """
    defn = c.get_definition()
    return (defn is not None) and (defn == funcdecl)

def fully_qualified(c):
    """ Retrieve a fully qualified function name (with namespaces)
    """
    res = c.spelling
    c = c.semantic_parent
    while c.kind != CursorKind.TRANSLATION_UNIT:
        res = c.spelling + '::' + res
        c = c.semantic_parent
    return res

def find_funcs_and_calls(tu):
    """ Retrieve lists of function declarations and call expressions in a translation unit
    """
    filename = tu.cursor.spelling
    calls = []
    funcs = []
    for c in tu.cursor.walk_preorder():
        if c.location.file is None:
            pass
        elif c.location.file.name != filename:
            pass
        elif c.kind == CursorKind.CALL_EXPR:
            calls.append(c)
        elif c.kind == CursorKind.FUNCTION_DECL:
            funcs.append(c)
    return funcs, calls

idx = Index.create()
args =  '-x c++ --std=c++11'.split()
tu = idx.parse('tmp.cpp', args=args)
funcs, calls = find_funcs_and_calls(tu)
for f in funcs:
    print(fully_qualified(f), f.location)
    for c in calls:
        if is_function_call(f, c):
            print('-', c)
    print()

为了说明这种方法有多好,你需要一个更具挑战性的例子来解析:

// tmp.cpp
#include <iostream>
using namespace std;

namespace impl {
    int addition(int x, int y) {
        return x + y;
    }

    void f() {
        addition(2, 3);
    }
}

int addition (int a, int b) {
  int r;
  r=a+b;
  return r;
}

int main () {
  int z, q;
  z = addition (5,3);
  q = addition (5,5);
  cout << "The first result is " << z;
  cout << "The second result is " << q;
}

我得到了输出:

impl::addition
- <SourceLocation file 'tmp.cpp', line 10, column 9>

impl::f

addition
- <SourceLocation file 'tmp.cpp', line 22, column 7>
- <SourceLocation file 'tmp.cpp', line 23, column 7>

main

将其扩展以考虑更多类型的声明(IMO)将是非平凡的,并且它本身就是一个有趣的项目。

发表评论

鉴于对于此答案中的代码是否产生了我提供的结果存在一些疑问,我添加了gist of the code(再现此问题的内容)和一个非常您可以用来试验的最小vagrant machine image。启动机器后,您可以克隆要点,并使用以下命令重现答案:

git clone https://gist.github.com/AndrewWalker/daa2af23f34fe9a6acc2de579ec45535 find-func-decl-refs
cd find-func-decl-refs
export LD_LIBRARY_PATH=/usr/lib/llvm-3.8/lib/ && python3 main.py