真正让这个问题变得具有挑战性的是 C++ 的复杂性。
考虑一下 C++ 中可调用的内容:函数、lambda、函数调用运算符、成员函数、模板函数和成员模板函数。因此,在仅匹配调用表达式的情况下,您需要能够消除这些情况的歧义。
此外,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)将是不平凡的,并且它本身就是一个有趣的项目。
处理意见
鉴于存在一些关于此答案中的代码是否产生我提供的结果的问题,我添加了一个代码要点 https://gist.github.com/AndrewWalker/daa2af23f34fe9a6acc2de579ec45535(重现这个问题的内容)和一个非常小的vagrant https://www.vagrantup.com/ 机器图像 https://github.com/AndrewWalker/vagrant-libclang-minimal您可以用它来进行实验。机器启动后,您可以克隆要点,并使用以下命令重现答案:
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