2016-05-12 107 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