2016-05-12 20 views
7

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

例如:

#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(见下文),以返回addition的参考文献在线路15和16

我曾尝试这个(改编自here

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方法将如何? (我对libclang和Python非常感兴趣。)

我看过thisdef find_typerefs找到了某种类型的所有引用,但我不知道如何实现它以满足我的需要。

理想情况下,我希望能够获取任何声明的所有引用;不仅是功能,但是变量声明,声明的参数(如在本例中第7行ab以上),类声明等

编辑Andrew's评论,这里有一些细节对我的设置规范:

  • LLVM 3.8.0-Win64的
  • libclang-PY 3 3.8.1
  • Python3.5.1(在Windows中,我假设的CPython)
  • 对于args,我尝试了回答here中提出的问题以及another答案中的问题。

*请注意,鉴于我的小编程经验,我可以通过简短的解释它的工作原理来欣赏答案。

+0

不为铛/ Python,但看到http://stackoverflow.com/a/37149988/120163另一个变化上如何“找到所有对特定符号/运算符的引用” –

+0

@IraBaxter感谢您的链接,但我只关心通过'libclang'实现这一点。 – Karim

回答

6

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

考虑在C++中可调用的函数:函数,lambdas,函数调用操作符,成员函数,模板函数和成员模板函数。所以在匹配调用表达式的情况下,您需要能够消除这些情况的歧义。此外,libclang没有提供叮当声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(再现这个问题的内容)和非常小的vagrantmachine 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 
+1

非常感谢您的详细解答。奇怪的是,我相比你得到一个不同的输出: 'IMPL ::加成 - IMPL <在0x009AAC60 clang.cindex.Cursor对象>: :f 另外 - - clang.cindex.Cursor object at 0x009C3260> main '。 请注意,第22行和第23行没有任何结果。任何想法? – Karim

+0

你可以添加一个关于你正在使用哪个版本的clang和libclang的描述,你传递给解析的参数和选项集,以及你的目标平台的问题。如果你在Windows上,你还可以添加哪个Python发行版(cpython,anaconda)和版本信息类型'import sys;打印(sys.version)'在解释器 –

+2

LLVM 3.8.0-win64,libclang-py3 3.8.1,Windows中的Python3.5.1。不知道如何找出Python的分布;我假设它是CPython。对于参数,我尝试了你在这里的答案和另一个[答案]中的建议(http://stackoverflow.com/questions/37098725/parsing-with-libclang-unable-to-parse-某些-令牌-蟒-在窗口/ 37100397#37100397)。 – Karim