2

C++のソースコードでは、すべての関数が書き込みおよび読み取りを行うクラスフィールドを探したいと思います。 Clangフロントエンドを使ってこれを行う最善の方法は何ですか?これまでのところ、私はRecursiveASTVisitorを使用して文を解析しようとしたClangを使用して、すべてのメンバーフィールドの読み取り/書き込みを検索するにはどうすればよいですか?

を、しかしを追跡する。

(ただし、効率的なソリューションのための出発点は素晴らしいことだ。私はすべてのステップの詳細な説明を求めていませんよ)ノード接続は困難です。また、私は以下のようなものを追跡する方法を見つけ出すことはできません。

int& x = m_int_field; 
x++; 

これは明らかにm_int_fieldを修正します。ただ一つのStmtがあればそれは分かりません。 ASTのトラバーサル自体が不十分と思われます。

私のボーナスは、フィールドとサブフィールドを別々に数えることができます(たとえば、メンバー構造の3つのフィールドにアクセスするなど)。

例:

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()の初期化を受け付けないことに注意してください。

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

からASTを見てみると、これらのノードは、MemberExprノードがないことを示しています。ノードはCXXCtorInitializerなので、ノードを取得するにはcxxCtorInitializerマッチャーが必要です。おそらく、すべての異なるノードを見つけるためには複数のマッチャーが必要です。

関連する問題