2017-04-13 69 views
2

給定一個C++源代碼,我想找到每個函數寫入和讀取的類字段。使用Clang前端進行此操作的最佳方式是什麼?如何查找使用Clang的所有成員字段讀/寫?

(我不是要求的所有步驟的詳細說明,但對於有效的解決方案的起點將是巨大的。)

到目前爲止,我試圖解析使用RecursiveASTVisitor語句,但保持跟蹤節點連接很困難。另外,我無法弄清楚如何保持跟蹤的東西象下面這樣:

int& x = m_int_field; 
x++; 

這清楚地修改m_int_field;但給定一個Stmt這是不可能知道的;所以AST本身似乎不足。

對我來說,一個好處是能夠單獨計算字段和子字段(例如,訪問成員結構的三個字段)。

例子:

typedef struct Y { 
    int m_structfield1; 
    float m_structfield2; 
    Y() { 
     m_structfield1 = 0; 
     m_structfield2 = 1.0f; 
    } 
} Y; 
class X { 
    int m_field1; 
    std::string m_field2; 
    Y m_field3; 
public: 
    X() : m_field2("lel") {} 
    virtual ~X() {} 
    void func1 (std::string s) { 
     m_field1 += 2; 
     m_field2 = s; 
    } 
    int func2() { 
     return m_field1 + 5; 
    } 
    void func3 (Y& y) { 
     int& i = m_field1; 
     y.m_structfield2 = 1.2f + i++; 
    } 
    int func4() { 
     func3 (m_field3); 
     return m_field3.m_structfield1; 
    } 
}; 

應該返回

X::X() -> m_field1 (w), m_field3.m_structfield1 (w), m_field3.m_structfield2 (w) 
X::func1(std::string) -> m_field1 (r+w), m_field2 (w) 
X::func2() -> m_field1 (r) 
X::func3(Y&) -> m_field1 (r+w) 
X::func4() -> m_field1 (r+w), m_field3.m_structfield2 (w), m_field3.m_structfield1 (r) 

我們可以假設爲簡單起見,沒有繼承。

回答

3

我一直在收集一些examples of analyzing code with Clang's AST matchers。這裏有一個示例應用程序StructFieldUser,它報告結構的哪些字段被讀取或寫入,以及每個訪問發生的功能。它與你正在尋找的不同,但它可能是一個有用的參考點。它演示了提取和記錄這種信息,並說明了如何將所有碎片放在一起。

一般來說,AST匹配者的首選地點是this post by Eli Bendersky

要感受一下,將解決你的問題,你可能會clang-query練習的匹配:

$ clang-query example.cpp -- # the two dashes mean no compilation db 
clang-query> let m1 memberExpr() 
clang-query> m m1 

Match #1: 

/path/example.cpp:9:9: note: "root" binds here 
     m_structfield1 = 0; 
     ^~~~~~~~~~~~~~ 

Match #2: 

/path/example.cpp:10:9: note: "root" binds here 
     m_structfield2 = 1.0f; 
     ^~~~~~~~~~~~~~ 
... 
11 matches. 

然後你就可以開始連接到使用穿越的匹配其他節點。這使您可以捕獲相關的上下文,如引用的函數或類方法。將bind表達式添加到節點匹配器將幫助您準確查看匹配的內容。綁定節點也將訪問回調中的節點。

clang-query> let m2 memberExpr(hasAncestor(functionDecl().bind("fdecl"))).bind("mexpr") 
clang-query> m m2 

Match #1: 

/path/example.cpp/path/example.cpp:8:5: note: "fdecl" binds here 
    Y() { 
    ^~~~~~ 
/path/example.cpp:9:9: note: "mexpr" binds here 
     m_structfield1 = 0; 
     ^~~~~~~~~~~~~~ 
/path/example.cpp:9:9: note: "root" binds here 
     m_structfield1 = 0; 
     ^~~~~~~~~~~~~~ 

Match #2: 

/path/example.cpp:8:5: note: "fdecl" binds here 
    Y() { 
    ^~~~~~ 
/path/example.cpp:10:9: note: "mexpr" binds here 
     m_structfield2 = 1.0f; 
     ^~~~~~~~~~~~~~ 
/path/example.cpp:10:9: note: "root" binds here 
     m_structfield2 = 1.0f; 
     ^~~~~~~~~~~~~~ 
... 

可能需要一些工作來學習如何提取所需的確切節點。請注意,上述匹配器不會在X::X()中執行初始化。看看AST從

clang-check -ast-dump example.cpp -- 

顯示那些節點不是MemberExpr節點;他們是CXXCtorInitializer節點,所以需要cxxCtorInitializer匹配器來獲得這些節點。可能需要多個匹配器才能找到所有不同的節點。

相關問題