在libclang(Python)中查找特定函数声明的所有引用

我试图在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;
}

因此,对于源文件上面,我想对函数声明为addition在第5行,我想find_all_function_decl_references (见下文)返回的引用addition在线路15和16。

我试过了(从这里改编)

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非常libclang 。)

我已经看到这个def find_typerefs找到所有对某种类型的引用,但我不知道如何实现它以满足我的需要。

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

编辑按照Andrew的评论,这里是关于我的设置规范的一些细节:

  • LLVM 3.8.0-win64
  • libclang-py3 3.8.1
  • Python3.5.1(在Windows中,我假设CPython)
  • 对于args ,我尝试了这里的答案和另一个答案中的答案。
  • *请注意,鉴于我的小编程经验,我可以通过简单解释它的工作原理来欣赏答案。


    真正使这个问题具有挑战性的是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)将其扩展到考虑更多类型的声明将是不平凡的,而且它本身就是一个有趣的项目。

    处理评论

    考虑到这个答案中的代码是否产生了我提供的结果有一些问题,我已经添加了代码的重要性(重现了这个问题的内容)和一个可以使用的非常小的流浪机器映像试验。 一旦机器启动,您可以克隆要点,并使用以下命令重现答案:

    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
    
    链接地址: http://www.djcxy.com/p/94583.html

    上一篇: Find all references of specific function declaration in libclang (Python)

    下一篇: libclang: how to get token semantics