如何找到所有使用Clang读取/写入的成员字段? [英] How can I find all member field read/writes using Clang?

查看:82
本文介绍了如何找到所有使用Clang读取/写入的成员字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出C ++源代码,我想找到每个函数编写和读取的类字段.使用Clang前端执行此操作的最佳方法是什么?

Given a C++ source code, I want to find the class fields that every function writes and reads. What is the best way of doing this using the Clang frontend?

(我并不需要详细说明所有步骤;但是,一个有效解决方案的起点将是很好的.)

(I'm not asking for a detailed explanation of all the steps; however a starting point for an efficient solution would be great.)

到目前为止,我尝试使用RecursiveASTVisitor解析语句,但是很难跟踪节点连接.另外,我无法弄清楚如何跟踪如下内容:

So far I tried parsing statements using the RecursiveASTVisitor, but keeping track of node connections is difficult. Also, I cannot figure out how to keep track of something like below:

int& x = m_int_field;
x++;

这显然修改了m_int_field;但是给定一个Stmt是不可能的;因此,仅AST遍历似乎不够.

This clearly modifies m_int_field; but given a single Stmt it is impossible to know that; so AST traversal by itself seems insufficient.

对我来说,一个好处是能够分别计算字段和子字段(例如,访问成员结构的三个字段).

A bonus for me is being able to separately count fields and sub-fields (e.g. Accessing three fields of a member struct).

示例:

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)

为简单起见,我们可以假设没有继承.

We can assume for simplicity that there is no inheritance.

推荐答案

我一直在收集一些分析代码的示例与Clang的AST匹配器一起使用.那里有一个示例应用程序StructFieldUser,它报告读取或写入结构的哪些字段以及发生每次访问的功能.它与您要查找的内容不同,但这可能是有用的参考点.它演示了如何提取和记录此类信息,并演示了如何将所有信息组合在一起.

I've been collecting some examples of analyzing code with Clang's AST matchers. There is an example application there, StructFieldUser, that reports which fields of a struct get read or written, and the function in which each access happens. It's different than what you're looking for, but it might be a useful point of reference. It demonstrates extracting and recording this kind of information, and it illustrates how to put all the pieces together.

要想了解可以解决您问题的匹配器,可以使用clang-query进行练习:

To get a feel for the matchers that would solve your problem, you might practice with 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表达式添加到节点匹配器将帮助您准确了解要匹配的内容.绑定节点还可以访问回调中的节点.

Then you can start to connect to other nodes using traversal matchers. This lets you capture related context, like the function or class method in which the reference is made. Adding bind expressions to the node matchers will help you see exactly what is getting matched. Binding nodes will also give access to the nodes in callbacks.

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()中进行初始化.从

It can take some work to learn how to pick up the exact nodes you need. Note that the matchers above don't pick up the initialization in X::X(). Looking at the AST from

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

表示那些节点不是MemberExpr节点;它们是CXXCtorInitializer节点,因此需要cxxCtorInitializer匹配器才能获取这些节点.可能需要多个匹配器才能找到所有不同的节点.

shows that those nodes are not MemberExpr nodes; they're CXXCtorInitializer nodes, so the cxxCtorInitializer matcher is needed to get those nodes. Multiple matchers are probably needed to find all the different nodes.

这篇关于如何找到所有使用Clang读取/写入的成员字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆