我想使用以下方法在代码库中查找 setter 和 getter 方法 Clang AST 匹配器 表达。 例如,这段代码有一个 getter 和一个 setter 报告:
struct S {
int m_x;
int getX() // getter
{
return m_x;
}
void setX(int x) // setter
{
m_x = x;
}
};
对于这两种方法,我想检查主体是否有一个 陈述。 getter 主体应该是带有 return 的 return 语句 班级成员的价值。 setter 主体应该是一个赋值 将传递的参数分配给类成员的语句。 (这 在实践中可能不是一套足够宽容的标准,但我会 对此作为第一个近似值感到满意。)
我发现我可以使用
cxxMethodDecl
匹配器来查找所有方法,
但目前尚不清楚如何挖掘尸体或检查各种
特性。 文档中的示例(上面链接)都没有
这;最接近的似乎是例子:
cxxConstructorDecl(
isCopyConstructor()
).bind("prepend_explicit")
但这似乎依赖于原始匹配器的存在
isCopyConstructor
对方法体进行分类(或者可能只是它的
签名?该文档没有说明它们实际上做了什么),
并且没有 isSetter
或类似的。
如何编写匹配表达式来查找所有 setter 和 getter 在翻译单位?
我将这个问题解释为 Clang AST 匹配器 它将报告 setter 和 getter。 这样的匹配器可以在以下位置进行测试 命令行使用
clang-query
.
以下 shell 脚本包含一个匹配表达式,该表达式将在 至少有一些这样的函数的情况(具体取决于它们是如何 书面)。 注释解释了每个部分的作用,所以应该是 可根据需要调整:
#!/bin/sh
PATH=/d/opt/clang+llvm-18.1.8-msvc/bin:$PATH
matcher='
cxxMethodDecl( # Report C++ method declarations
hasBody( # where the body
compoundStmt( # is a compound statement
statementCountIs(1), # with one contained statement
hasAnySubstatement( # that
anyOf( # is either:
returnStmt( # (1) a return statement
hasReturnValue( # whose return value
implicitCastExpr( # is an implicit conversion
hasSourceExpression( # of
memberExpr( # a class member
hasObjectExpression( # of the object
cxxThisExpr() # `*this`, or
)
)
)
)
)
),
binaryOperator( # (2) is a binary expression
isAssignmentOperator(), # using the assignment operator
hasLHS( # where the left-hand side
memberExpr( # is a class member
hasObjectExpression( # of the object
cxxThisExpr() # `*this`, and
)
)
),
hasRHS( # where the right-hand side
implicitCastExpr( # is an implicit conversion
hasSourceExpression( # of
declRefExpr( # a reference to a declaration
hasDeclaration( # of
parmVarDecl() # a parameter.
)
)
)
)
)
)
)
)
)
)
)
'
clang-query \
-c "m $matcher" \
test.cc --
# EOF
为了测试这个匹配器,我使用了这个测试用例:
// test.cc
// Testcases for a matcher to find accessor methods.
struct S {
int m_x;
int getX()
{
return m_x;
}
void setX(int x)
{
m_x = x;
}
};
// EOF
有这个 AST:
$ clang -fsyntax-only -Xclang -ast-dump test.cc
TranslationUnitDecl 0x23df46300a0 <<invalid sloc>> <invalid sloc>
|-CXXRecordDecl 0x23df4630900 <<invalid sloc>> <invalid sloc> implicit struct _GUID
| `-TypeVisibilityAttr 0x23df46309b0 <<invalid sloc>> Implicit Default
|-TypedefDecl 0x23df4630a28 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0x23df4630670 '__int128'
|-TypedefDecl 0x23df4630a98 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0x23df4630690 'unsigned __int128'
|-TypedefDecl 0x23df4630e40 <<invalid sloc>> <invalid sloc> implicit __NSConstantString '__NSConstantString_tag'
| `-RecordType 0x23df4630b80 '__NSConstantString_tag'
| `-CXXRecord 0x23df4630af0 '__NSConstantString_tag'
|-CXXRecordDecl 0x23df4630e98 <<invalid sloc>> <invalid sloc> implicit class type_info
| `-TypeVisibilityAttr 0x23df4630f50 <<invalid sloc>> Implicit Default
|-TypedefDecl 0x23df4630fc8 <<invalid sloc>> <invalid sloc> implicit size_t 'unsigned long long'
| `-BuiltinType 0x23df4630290 'unsigned long long'
|-TypedefDecl 0x23df466b568 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0x23df4631020 'char *'
| `-BuiltinType 0x23df4630150 'char'
|-TypedefDecl 0x23df466b5d8 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list 'char *'
| `-PointerType 0x23df4631020 'char *'
| `-BuiltinType 0x23df4630150 'char'
`-CXXRecordDecl 0x23df466b630 <test.cc:4:1, line:16:1> line:4:8 struct S definition
|-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
| |-DefaultConstructor exists trivial needs_implicit
| |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| |-MoveConstructor exists simple trivial needs_implicit
| |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| |-MoveAssignment exists simple trivial needs_implicit
| `-Destructor simple irrelevant trivial needs_implicit
|-CXXRecordDecl 0x23df466b748 <col:1, col:8> col:8 implicit struct S
|-FieldDecl 0x23df466b7f0 <line:5:3, col:7> col:7 referenced m_x 'int'
|-CXXMethodDecl 0x23df466b8d8 <line:7:3, line:10:3> line:7:7 getX 'int ()' implicit-inline
| `-CompoundStmt 0x23df466bba0 <line:8:3, line:10:3>
| `-ReturnStmt 0x23df466bb90 <line:9:5, col:12>
| `-ImplicitCastExpr 0x23df466bb78 <col:12> 'int' <LValueToRValue>
| `-MemberExpr 0x23df466bb48 <col:12> 'int' lvalue ->m_x 0x23df466b7f0
| `-CXXThisExpr 0x23df466bb38 <col:12> 'S *' implicit this
`-CXXMethodDecl 0x23df466ba70 <line:12:3, line:15:3> line:12:8 setX 'void (int)' implicit-inline
|-ParmVarDecl 0x23df466b998 <col:13, col:17> col:17 used x 'int'
`-CompoundStmt 0x23df466bc98 <line:13:3, line:15:3>
`-BinaryOperator 0x23df466bc78 <line:14:5, col:11> 'int' lvalue '='
|-MemberExpr 0x23df466bc10 <col:5> 'int' lvalue ->m_x 0x23df466b7f0
| `-CXXThisExpr 0x23df466bc00 <col:5> 'S *' implicit this
`-ImplicitCastExpr 0x23df466bc60 <col:11> 'int' <LValueToRValue>
`-DeclRefExpr 0x23df466bc40 <col:11> 'int' lvalue ParmVar 0x23df466b998 'x' 'int'
脚本产生以下输出:
Match #1:
$PWD\test.cc:7:3: note: "root"
binds here
7 | int getX()
| ^~~~~~~~~~
8 | {
| ~
9 | return m_x;
| ~~~~~~~~~~~
10 | }
| ~
Match #2:
$PWD\test.cc:12:3: note: "root"
binds here
12 | void setX(int x)
| ^~~~~~~~~~~~~~~~
13 | {
| ~
14 | m_x = x;
| ~~~~~~~~
15 | }
| ~
2 matches.
创建匹配器的过程基本上遵循 AST 逐行转储,将每个我想要匹配的元素变成它的 对应的匹配器。 在某些情况下,这很简单(对于 例如,
CXXMethodDecl
AST 节点与 cxxMethodDecl
匹配
matcher),而对于其他人,我必须在
匹配器参考,以及反复试验,以找到正确的
组合。