kub*_*tto 6 c++ static-analysis clang abstract-syntax-tree llvm-clang
给定一个C++源代码,我想找到每个函数写入和读取的类字段.使用Clang前端的最佳方法是什么?
(我不是要求对所有步骤进行详细解释;但是,有效解决方案的出发点会很棒.)
到目前为止,我尝试使用RecursiveASTVisitor解析语句,但是跟踪节点连接很困难.另外,我无法弄清楚如何跟踪下面的内容:
int& x = m_int_field;
x++;
Run Code Online (Sandbox Code Playgroud)
这明显改变了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;
}
};
Run Code Online (Sandbox Code Playgroud)
应该回来
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)
Run Code Online (Sandbox Code Playgroud)
我们可以简单地假设没有继承.
我一直在收集一些使用Clang的AST匹配器分析代码的示例。那里有一个示例应用程序StructFieldUser,它报告读取或写入结构的哪些字段以及发生每次访问的功能。它与您要查找的内容不同,但这可能是有用的参考点。它演示了如何提取和记录此类信息,并演示了如何将所有信息组合在一起。
通常,从AST匹配器开始的一个好地方是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.
Run Code Online (Sandbox Code Playgroud)
然后,您可以开始使用遍历匹配器连接到其他节点。这使您可以捕获相关的上下文,例如进行引用的函数或类方法。将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;
^~~~~~~~~~~~~~
...
Run Code Online (Sandbox Code Playgroud)
学习如何挑选所需的确切节点可能需要花费一些工作。请注意,上面的匹配器不会选择中的初始化X::X()
。从看AST
clang-check -ast-dump example.cpp --
Run Code Online (Sandbox Code Playgroud)
表明那些节点不是MemberExpr
节点;它们是CXXCtorInitializer
节点,因此cxxCtorInitializer
需要匹配器来获取这些节点。可能需要多个匹配器才能找到所有不同的节点。
归档时间: |
|
查看次数: |
1134 次 |
最近记录: |