【问题标题】:Matching private class members using clang AST Matcher使用 clang AST Matcher 匹配私有类成员
【发布时间】:2015-03-16 21:27:09
【问题描述】:

我正在编写一个 Clang 工具来静态分析源文件,并匹配和重命名类的所有私有成员。

考虑一个例子:

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 声明。我想摆脱这场比赛。

匹配器返回一个指向MemberExpr 对象的指针。我试图通过检查 isArrow() 谓词来过滤第三个匹配项,它有所帮助,但是我无法匹配带有点的表达式,例如 cl.a

我正在寻找其他 AST 匹配器表达式或一些对 MemberExpr 对象进行操作的代码,并访问源文件中的所有私有变量,仅此而已。

【问题讨论】:

  • 这可能是 clang 给隐式定义的复制构造函数的位置吗?我不熟悉 clang 是如何做到的,但我熟悉类似的工具。一种常见的方法是让隐式声明/定义的函数获取构造函数的位置。也许您可以在未隐式定义的封闭函数上添加过滤器?
  • @RichardCorden,我尝试使用 memberExpr(hasDeclaration(namedDecl(isPrivate(), unless(isImplicit())))) 匹配表达式,但没有任何影响。
  • 我对 Clang API 一点也不熟悉。你不想问a 是否是隐式的,你想问包含表达式的函数是否是隐式定义的。你能得到那个信息吗?检验我的理论的一种方法是显式定义复制构造函数。如果您不再在源文件的第 1 行获得匹配项,则表明它一直是复制构造函数的位置。
  • 你是对的。如果显式定义了复制构造函数,则第三个匹配项将消失。
  • 嘿,我目前正在尝试做一些类似的事情,并且对 llvm/clang 来说是全新的 - 你能为我提供一个你的项目的链接,或者告诉我你是如何匹配第一名?我目前正在匹配 fieldDecl(hasAncestor(cxxRecordDecl())) 但不知道这在所有情况下是否都安全......

标签: c++ clang static-analysis abstract-syntax-tree


【解决方案1】:

就像您观察到的那样,问题是由于隐式定义的副本 构造函数。这是 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>

复制构造函数在&lt;line:1:7&gt; 处分配了一个定义。这里 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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 2020-10-02
    • 2011-02-24
    相关资源
    最近更新 更多