确定成员调用在 Clang AST 中是否是虚拟的
Determine if a member call is virtual in the Clang AST
我想编写一个分析器来计算 virtual
函数调用,方法是查看 C++ AST(-ast-dump
的输出),但我很难确定哪些函数调用是 virtual
而哪些不是。这是一段代码示例:
struct A {
A() {}
virtual int foo() { return 0; }
};
struct B : public A {
B() {}
//virtual int foo() { return 3; }
};
struct C : public B {
C() {}
virtual int foo() { return 1; }
};
int test(C* c) {
return c->foo() + c->B::foo() + c->A::foo();
}
在我最初的实现中,我只是使用 expr->getMethodDecl()->isVirtual()
检查被调用的函数是否是 virtual
,但是(据我了解)c->B::foo()
和 c->A::foo()
实际上不是 virtual
所以仅仅询问被调用的函数是否是 virtual
.
是不够的
当我从这段代码中转储 AST 时,我得到了 test
的以下树:
`-FunctionDecl 0x1d33620 <line:19:1, line:21:1> line:19:5 test 'int (C *)'
|-ParmVarDecl 0x1d33558 <col:10, col:13> col:13 used c 'C *'
`-CompoundStmt 0x1d339f0 <col:16, line:21:1>
`-ReturnStmt 0x1d339e0 <line:20:5, col:47>
`-BinaryOperator 0x1d339c0 <col:12, col:47> 'int' '+'
|-BinaryOperator 0x1d338a8 <col:12, col:33> 'int' '+'
| |-CXXMemberCallExpr 0x1d33778 <col:12, col:19> 'int'
| | `-MemberExpr 0x1d33748 <col:12, col:15> '<bound member function type>' ->foo 0x1d31c80
| | `-ImplicitCastExpr 0x1d33730 <col:12> 'C *' <LValueToRValue>
| | `-DeclRefExpr 0x1d33710 <col:12> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'
| `-CXXMemberCallExpr 0x1d33848 <col:23, col:33> 'int'
| `-MemberExpr 0x1d33800 <col:23, col:29> '<bound member function type>' ->foo 0x1d02400
| `-ImplicitCastExpr 0x1d33888 <col:23> 'A *' <UncheckedDerivedToBase (A)>
| `-ImplicitCastExpr 0x1d33868 <col:23> 'B *' <UncheckedDerivedToBase (B)>
| `-ImplicitCastExpr 0x1d337d8 <col:23> 'C *' <LValueToRValue>
| `-DeclRefExpr 0x1d33798 <col:23> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'
`-CXXMemberCallExpr 0x1d33978 <col:37, col:47> 'int'
`-MemberExpr 0x1d33930 <col:37, col:43> '<bound member function type>' ->foo 0x1d02400
`-ImplicitCastExpr 0x1d33998 <col:37> 'A *' <UncheckedDerivedToBase (B -> A)>
`-ImplicitCastExpr 0x1d33908 <col:37> 'C *' <LValueToRValue>
`-DeclRefExpr 0x1d338c8 <col:37> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'
从这里看,似乎 UncheckedDerivedToBase
转换标记了函数调用非 virtual
的地方。总是这样吗?我是否应该始终将 CXXMemberCallExpr (MemberExpr (ImplicitCastExpr<UncheckedDerivedToBase> e)))
形式的调用视为非 virtual
调用?是否有其他模式表明非 virtual
函数调用?有没有更可靠的方法来确定这个事实?
编辑:更多的调查表明上述UncheckedDerivedToBase
的假设是不充分的。此代码:
struct A {
virtual int foo() { return 100; }
};
struct B : public A {
virtual int foo() { return 10; }
};
int test(B* b) {
return b->foo() + b->B::foo();
}
似乎为两个调用生成完全相同的 AST 节点(至少在控制台上无法区分),但如果 b
实际上是派生的 class,则根据标准语义应该不同,例如C
以上。
区别因素是被调用对象的MemberExpr
上的hasQualifier
。如果hasQualifier
是true
,那么函数调用是非virtual
.
我想编写一个分析器来计算 virtual
函数调用,方法是查看 C++ AST(-ast-dump
的输出),但我很难确定哪些函数调用是 virtual
而哪些不是。这是一段代码示例:
struct A {
A() {}
virtual int foo() { return 0; }
};
struct B : public A {
B() {}
//virtual int foo() { return 3; }
};
struct C : public B {
C() {}
virtual int foo() { return 1; }
};
int test(C* c) {
return c->foo() + c->B::foo() + c->A::foo();
}
在我最初的实现中,我只是使用 expr->getMethodDecl()->isVirtual()
检查被调用的函数是否是 virtual
,但是(据我了解)c->B::foo()
和 c->A::foo()
实际上不是 virtual
所以仅仅询问被调用的函数是否是 virtual
.
当我从这段代码中转储 AST 时,我得到了 test
的以下树:
`-FunctionDecl 0x1d33620 <line:19:1, line:21:1> line:19:5 test 'int (C *)'
|-ParmVarDecl 0x1d33558 <col:10, col:13> col:13 used c 'C *'
`-CompoundStmt 0x1d339f0 <col:16, line:21:1>
`-ReturnStmt 0x1d339e0 <line:20:5, col:47>
`-BinaryOperator 0x1d339c0 <col:12, col:47> 'int' '+'
|-BinaryOperator 0x1d338a8 <col:12, col:33> 'int' '+'
| |-CXXMemberCallExpr 0x1d33778 <col:12, col:19> 'int'
| | `-MemberExpr 0x1d33748 <col:12, col:15> '<bound member function type>' ->foo 0x1d31c80
| | `-ImplicitCastExpr 0x1d33730 <col:12> 'C *' <LValueToRValue>
| | `-DeclRefExpr 0x1d33710 <col:12> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'
| `-CXXMemberCallExpr 0x1d33848 <col:23, col:33> 'int'
| `-MemberExpr 0x1d33800 <col:23, col:29> '<bound member function type>' ->foo 0x1d02400
| `-ImplicitCastExpr 0x1d33888 <col:23> 'A *' <UncheckedDerivedToBase (A)>
| `-ImplicitCastExpr 0x1d33868 <col:23> 'B *' <UncheckedDerivedToBase (B)>
| `-ImplicitCastExpr 0x1d337d8 <col:23> 'C *' <LValueToRValue>
| `-DeclRefExpr 0x1d33798 <col:23> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'
`-CXXMemberCallExpr 0x1d33978 <col:37, col:47> 'int'
`-MemberExpr 0x1d33930 <col:37, col:43> '<bound member function type>' ->foo 0x1d02400
`-ImplicitCastExpr 0x1d33998 <col:37> 'A *' <UncheckedDerivedToBase (B -> A)>
`-ImplicitCastExpr 0x1d33908 <col:37> 'C *' <LValueToRValue>
`-DeclRefExpr 0x1d338c8 <col:37> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'
从这里看,似乎 UncheckedDerivedToBase
转换标记了函数调用非 virtual
的地方。总是这样吗?我是否应该始终将 CXXMemberCallExpr (MemberExpr (ImplicitCastExpr<UncheckedDerivedToBase> e)))
形式的调用视为非 virtual
调用?是否有其他模式表明非 virtual
函数调用?有没有更可靠的方法来确定这个事实?
编辑:更多的调查表明上述UncheckedDerivedToBase
的假设是不充分的。此代码:
struct A {
virtual int foo() { return 100; }
};
struct B : public A {
virtual int foo() { return 10; }
};
int test(B* b) {
return b->foo() + b->B::foo();
}
似乎为两个调用生成完全相同的 AST 节点(至少在控制台上无法区分),但如果 b
实际上是派生的 class,则根据标准语义应该不同,例如C
以上。
区别因素是被调用对象的MemberExpr
上的hasQualifier
。如果hasQualifier
是true
,那么函数调用是非virtual
.