10

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 行目の関数宣言については、15 行目と 16 行目find_all_function_decl_referencesの参照を (下記参照) に返したいと思います。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

もう 1 つの方法は、リストにあるすべての関数宣言を保存し、find_all_function_decl_referencesそれぞれに対してメソッドを実行することです。

これにアプローチする方法を知っている人はいますか?このfind_all_function_decl_references方法はどうでしょう。(私はlibclangPythonと非常に新しいです。)

が何らかのタイプへのすべての参照を見つけているところを見てきましdef find_typerefsたが、私のニーズに合わせてそれを実装する方法がわかりません。

理想的には、あらゆる宣言のすべての参照を取得できるようにしたいと考えています。関数だけでなく、変数の宣言、パラメーターの宣言 (例: 上記の例の 7 行目のaand b)、クラスの宣言など。

編集アンドリューのコメントに 続いて、私のセットアップ仕様に関する詳細を以下に示します。

  • LLVM 3.8.0-win64
  • libclang-py3 3.8.1
  • Python3.5.1 (WindowsではCPythonを想定)
  • については、こちらの回答で提案されているものと別のargs回答のも​​のの両方を試しました。

*私の小さなプログラミング経験を考えると、それがどのように機能するかについての簡単な説明とともに回答をいただければ幸いです.

4

1 に答える 1

7

この問題を本当に難しくしているのは、C++ の複雑さです。

C++ で呼び出し可能なものを検討してください: 関数、ラムダ、関数呼び出し演算子、メンバー関数、テンプレート関数、およびメンバー テンプレート関数。したがって、呼び出し式を一致させるだけの場合は、これらのケースを明確にする必要があります。

さらに、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) 自明ではなく、それ自体が興味深いプロジェクトです。

コメントへの対処

この回答のコードが私が提供した結果を生成するかどうかについていくつかの質問があることを考慮して、コードの要点(この質問の内容を再現するもの) と、使用できる非常に最小限のvagrant マシン イメージを追加しました。実験する。マシンが起動したら、gist のクローンを作成し、次のコマンドで回答を再現できます。

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
于 2016-05-13T12:57:59.993 に答える