使用 clang AST 匹配器匹配私有 class 成员
Matching private class members using clang AST Matcher
我正在编写一个 Clang 工具来静态分析源文件,并匹配和重命名 class.
的所有私有成员
考虑一个例子:
class AClass { // problem: my matcher modifies AST node here too
private:
int a; // <- I know how to rename this 'a' using other matcher
public:
AClass() {
AClass cl;
this->a = 1; // <- rename this 'a'
cl.a = 2; // <- rename this 'a'
}
};
void bar(AClass);
void foo() {
//bar(AClass());
}
我使用以下匹配器来访问我想要修改的 AST 节点。效果如我所料。
clang-query> match memberExpr(hasDeclaration(namedDecl(isPrivate())))
Match #1:
sum.cpp:7:9: note: "root" binds here
this->a = 1;
^~~~~~~
Match #2:
sum.cpp:8:9: note: "root" binds here
cl.a = 2;
^~~~
2 matches.
如果在示例中我取消注释 bar(AClass());
行,就会出现问题。多了一个匹配项,恰好
Match #3:
sum.cpp:1:7: note: "root" binds here
class AClass {
^~~~~~
3 matches.
这导致以一种奇怪的方式重写 class AClass
声明。我想摆脱这场比赛。
匹配器 returns 指向 MemberExpr
对象的指针。我试图通过检查 isArrow()
谓词来过滤第三个匹配项,它有所帮助,但后来我无法将表达式与点匹配,例如 cl.a
.
我正在寻找其他 AST 匹配器表达式或一些对 MemberExpr
对象进行操作的代码,并访问源文件中的所有私有变量,仅此而已。
正如您所观察到的,问题是由于隐式定义的副本造成的
构造函数。这是 AST 的转储,
% clang-check -ast-dump -ast-dump-filter=AClass s.cpp --
Dumping AClass:
CXXRecordDecl 0x2cb3700 </tmp/s.cpp:1:1, line:10:1> line:1:7 referenced class AClass definition
|-CXXRecordDecl 0x2cb3810 <col:1, col:7> col:7 implicit referenced class AClass
|-AccessSpecDecl 0x2cb38a0 <line:2:1, col:8> col:1 private
|-FieldDecl 0x2cb38e0 <line:3:3, col:7> col:7 referenced a 'int'
|-AccessSpecDecl 0x2cb3930 <line:4:1, col:7> col:1 public
|-CXXConstructorDecl 0x2cfaee0 <line:5:3, line:9:3> line:5:3 used AClass 'void (void)'
| `-CompoundStmt 0x2cfb440 <col:12, line:9:3>
| |-DeclStmt 0x2cfb240 <line:6:5, col:14>
| | `-VarDecl 0x2cfafe0 <col:5, col:12> col:12 used cl 'class AClass' callinit
| | `-CXXConstructExpr 0x2cfb210 <col:12> 'class AClass' 'void (void)'
| |-BinaryOperator 0x2cfb2c0 <line:7:5, col:15> 'int' lvalue '='
| | |-MemberExpr 0x2cfb270 <col:5, col:11> 'int' lvalue ->a 0x2cb38e0
| | | `-CXXThisExpr 0x2cfb258 <col:5> 'class AClass *' this
| | `-IntegerLiteral 0x2cfb2a0 <col:15> 'int' 1
| `-BinaryOperator 0x2cfb360 <line:8:5, col:12> 'int' lvalue '='
| |-MemberExpr 0x2cfb310 <col:5, col:8> 'int' lvalue .a 0x2cb38e0
| | `-DeclRefExpr 0x2cfb2e8 <col:5> 'class AClass' lvalue Var 0x2cfafe0 'cl' 'class AClass'
| `-IntegerLiteral 0x2cfb340 <col:12> 'int' 2
|-CXXConstructorDecl 0x2cfb070 <line:1:7> col:7 implicit used AClass 'void (const class AClass &) throw()' inline
| |-ParmVarDecl 0x2cfb1b0 <col:7> col:7 used 'const class AClass &'
| |-CXXCtorInitializer Field 0x2cb38e0 'a' 'int'
| | `-ImplicitCastExpr 0x2cfb9e0 <col:7> 'int' <LValueToRValue>
| | `-MemberExpr 0x2cfb998 <col:7> 'const int' lvalue .a 0x2cb38e0
| | `-DeclRefExpr 0x2cfb970 <col:7> 'const class AClass' lvalue ParmVar 0x2cfb1b0 '' 'const class AClass &'
| `-CompoundStmt 0x2cfba28 <col:7>
`-CXXDestructorDecl 0x2cfb770 <col:7> col:7 implicit used ~AClass 'void (void) throw()' inline
`-CompoundStmt 0x2cfb890 <col:7>
其中复制构造函数在 <line:1:7>
处分配了一个定义。这里的
MemberExpr
你不小心匹配到了隐式副本中
构造函数。就像理查德科恩在他的评论中写的那样, MemberExpr
不是
隐含的,但它的祖先,复制构造函数。您可以删除这些不需要的
像建议的那样通过过滤包含的内容进行匹配,
match memberExpr(hasDeclaration(namedDecl(isPrivate())),
unless(hasAncestor(isImplicit())))
这会让您找到想要的比赛,
Match #1:
/tmp/s.cpp:7:5: note: "root" binds here
this->a = 1; // <- rename this 'a'
^~~~~~~
Match #2:
/tmp/s.cpp:8:5: note: "root" binds here
cl.a = 2; // <- rename this 'a'
^~~~
2 matches.
我正在编写一个 Clang 工具来静态分析源文件,并匹配和重命名 class.
的所有私有成员考虑一个例子:
class AClass { // problem: my matcher modifies AST node here too
private:
int a; // <- I know how to rename this 'a' using other matcher
public:
AClass() {
AClass cl;
this->a = 1; // <- rename this 'a'
cl.a = 2; // <- rename this 'a'
}
};
void bar(AClass);
void foo() {
//bar(AClass());
}
我使用以下匹配器来访问我想要修改的 AST 节点。效果如我所料。
clang-query> match memberExpr(hasDeclaration(namedDecl(isPrivate())))
Match #1:
sum.cpp:7:9: note: "root" binds here
this->a = 1;
^~~~~~~
Match #2:
sum.cpp:8:9: note: "root" binds here
cl.a = 2;
^~~~
2 matches.
如果在示例中我取消注释 bar(AClass());
行,就会出现问题。多了一个匹配项,恰好
Match #3:
sum.cpp:1:7: note: "root" binds here
class AClass {
^~~~~~
3 matches.
这导致以一种奇怪的方式重写 class AClass
声明。我想摆脱这场比赛。
匹配器 returns 指向 MemberExpr
对象的指针。我试图通过检查 isArrow()
谓词来过滤第三个匹配项,它有所帮助,但后来我无法将表达式与点匹配,例如 cl.a
.
我正在寻找其他 AST 匹配器表达式或一些对 MemberExpr
对象进行操作的代码,并访问源文件中的所有私有变量,仅此而已。
正如您所观察到的,问题是由于隐式定义的副本造成的 构造函数。这是 AST 的转储,
% clang-check -ast-dump -ast-dump-filter=AClass s.cpp --
Dumping AClass:
CXXRecordDecl 0x2cb3700 </tmp/s.cpp:1:1, line:10:1> line:1:7 referenced class AClass definition
|-CXXRecordDecl 0x2cb3810 <col:1, col:7> col:7 implicit referenced class AClass
|-AccessSpecDecl 0x2cb38a0 <line:2:1, col:8> col:1 private
|-FieldDecl 0x2cb38e0 <line:3:3, col:7> col:7 referenced a 'int'
|-AccessSpecDecl 0x2cb3930 <line:4:1, col:7> col:1 public
|-CXXConstructorDecl 0x2cfaee0 <line:5:3, line:9:3> line:5:3 used AClass 'void (void)'
| `-CompoundStmt 0x2cfb440 <col:12, line:9:3>
| |-DeclStmt 0x2cfb240 <line:6:5, col:14>
| | `-VarDecl 0x2cfafe0 <col:5, col:12> col:12 used cl 'class AClass' callinit
| | `-CXXConstructExpr 0x2cfb210 <col:12> 'class AClass' 'void (void)'
| |-BinaryOperator 0x2cfb2c0 <line:7:5, col:15> 'int' lvalue '='
| | |-MemberExpr 0x2cfb270 <col:5, col:11> 'int' lvalue ->a 0x2cb38e0
| | | `-CXXThisExpr 0x2cfb258 <col:5> 'class AClass *' this
| | `-IntegerLiteral 0x2cfb2a0 <col:15> 'int' 1
| `-BinaryOperator 0x2cfb360 <line:8:5, col:12> 'int' lvalue '='
| |-MemberExpr 0x2cfb310 <col:5, col:8> 'int' lvalue .a 0x2cb38e0
| | `-DeclRefExpr 0x2cfb2e8 <col:5> 'class AClass' lvalue Var 0x2cfafe0 'cl' 'class AClass'
| `-IntegerLiteral 0x2cfb340 <col:12> 'int' 2
|-CXXConstructorDecl 0x2cfb070 <line:1:7> col:7 implicit used AClass 'void (const class AClass &) throw()' inline
| |-ParmVarDecl 0x2cfb1b0 <col:7> col:7 used 'const class AClass &'
| |-CXXCtorInitializer Field 0x2cb38e0 'a' 'int'
| | `-ImplicitCastExpr 0x2cfb9e0 <col:7> 'int' <LValueToRValue>
| | `-MemberExpr 0x2cfb998 <col:7> 'const int' lvalue .a 0x2cb38e0
| | `-DeclRefExpr 0x2cfb970 <col:7> 'const class AClass' lvalue ParmVar 0x2cfb1b0 '' 'const class AClass &'
| `-CompoundStmt 0x2cfba28 <col:7>
`-CXXDestructorDecl 0x2cfb770 <col:7> col:7 implicit used ~AClass 'void (void) throw()' inline
`-CompoundStmt 0x2cfb890 <col:7>
其中复制构造函数在 <line:1:7>
处分配了一个定义。这里的
MemberExpr
你不小心匹配到了隐式副本中
构造函数。就像理查德科恩在他的评论中写的那样, MemberExpr
不是
隐含的,但它的祖先,复制构造函数。您可以删除这些不需要的
像建议的那样通过过滤包含的内容进行匹配,
match memberExpr(hasDeclaration(namedDecl(isPrivate())),
unless(hasAncestor(isImplicit())))
这会让您找到想要的比赛,
Match #1:
/tmp/s.cpp:7:5: note: "root" binds here
this->a = 1; // <- rename this 'a'
^~~~~~~
Match #2:
/tmp/s.cpp:8:5: note: "root" binds here
cl.a = 2; // <- rename this 'a'
^~~~
2 matches.