中查找特定函数声明的所有引用

中查找特定函数声明的所有引用

本文介绍了在libclang(Python)中查找特定函数声明的所有引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to find (line and column position) all the references of a specific function declaration when parsing a C++ source file via libclang in 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(请参见下文)在第15和16行返回addition的引用.

So, for the source file above, I would like for the function declaration for addition in line 5, I would like the find_all_function_decl_references(see below) to return the references of addition at lines 15 and 16.

我已经尝试过此操作(改编自此处)

I have tried this (adapted from 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方法.

Another approach could be to store all the function declarations found on a list and run the find_all_function_decl_references method on each.

有人对如何解决这个问题有任何想法吗? find_all_function_decl_references方法将如何? (我对libclang和Python非常陌生.)

Does anyone has any idea of how to approach this? How this find_all_function_decl_references method would be? (I am very new with libclang and Python.)

我看过 def find_typerefs正在查找对某种类型的所有引用,但我不确定如何根据需要实现它.

I have seen this where the def find_typerefs is finding all references to some type but I am not sure how to implement it for my needs.

理想,我希望能够获取所有声明的所有引用;不仅是函数,还包括变量声明,参数声明(例如,上面第7行中的示例中的ab),类声明等.

Ideally, I would like to be able to fetch all references for any declaration; not only functions but variable declarations, parameter declarations (e.g. the a and b in the example above in line 7), class declarations etc.

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

EDITFollowing Andrew's comment, here are some details regarding my setup specifications:

  • LLVM 3.8.0-win64
  • libclang-py3 3.8.1
  • Python3.5.1(在Windows中,我假设是CPython)
  • 对于args,我尝试了此处中的建议,也尝试了答案.
  • LLVM 3.8.0-win64
  • libclang-py3 3.8.1
  • Python3.5.1 (in Windows, I assume CPython)
  • For the args, I tried both the ones suggested in the answer here and the ones from another answer.

*请注意,由于我的编程经验很小,所以我对通过简要解释其工作原理的答案表示赞赏.

*Please note, given my small programming experience I could appreciate an answer with a brief explanation of how it works.

推荐答案

真正使此问题具有挑战性的是C ++的复杂性.

The thing that really makes this problem challenging is the complexity of C++.

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

Consider what is callable in C++: functions, lambdas, the function call operator, member functions, template functions and member template functions. So in the case of just matching call expressions, you'd need to be able to disambiguate these cases.

此外,libclang不能提供对cast AST的完美视图(某些节点无法完全公开,尤其是某些与模板相关的节点).因此,有可能(甚至有可能)任意代码片段包含某种构造,其中AST的libclangs视图不足以将调用表达式与声明相关联.

Furthermore, libclang doesn't offer a perfect view of the clang AST (some nodes don't get exposed completely, particularly some nodes related to templates). Consequently, it's possible (even likely) that an arbitrary code fragment would contain some construct where libclangs view of the AST was insufficient to associate the call expression with a declaration.

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

However, if you're prepared to restrict yourself to a subset of the language it may be possible to make some headway - for example, the following sample tries to associate call sites with function declarations. It does this by doing a single pass over all the nodes in the AST matching function declarations with call expressions.

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

要显示其效果如何,您需要一个更具挑战性的示例来进行解析:

To show how well this works, you need a slightly more challenging example to parse:

// 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)将是不平凡的,而且就其本身而言,这是一个有趣的项目.

Scaling this up to consider more types of declarations would (IMO) be non-trivial and an interesting project in it's own right.

发表评论

鉴于存在一些关于此答案中的代码是否产生我提供的结果的问题,我添加了代码要点(复制了该问题的内容)和一个非常小的 vagrant 您可以用来试验的机器图像.机器启动后,您可以克隆要点,并使用以下命令重现答案:

Given that there are some questions about whether the code in this answer produces the results I've provided, I've added a gist of the code (that reproduces the content of this question) and a very minimal vagrant machine image that you can use to experiment with. Once the machine is booted you can clone the gist, and reproduce the answer with the commands:

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

这篇关于在libclang(Python)中查找特定函数声明的所有引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 00:04