编写匹配器¶
在本页中,我们将提供一些关于匹配器是什么的信息,然后提供一个逐步开发简单匹配器的示例。
匹配器类型¶
匹配器有三种类型:节点匹配器、缩小匹配器和遍历匹配器。它们之间并不总是存在清晰的分界或区别,因此请将此解释视为说明性而非确定性的。以下是匹配器的文档:https://clang.llvm.net.cn/docs/LibASTMatchersReference.html
在该页面上,这一点并不明显,因此我们想指出,**单击匹配器的名称可以展开有关该匹配器的帮助信息。** 例如
节点匹配器¶
节点匹配器可以被认为是“名词”。它们指定您要匹配的节点的**类型**,即某个特定的事物。一个函数、一个二元运算符、一个变量、一个类型。
完整列表节点匹配器在文档中列出。一些常见的匹配器包括 functionDecl()
、binaryOperator()
和 stmt()
。
缩小匹配器¶
缩小匹配器可以被认为是“形容词”。它们缩小或描述一个节点,因此必须应用于节点匹配器。例如,一个节点匹配器可以是 functionDecl
,而应用于它的缩小匹配器可以是 parameterCountIs
。
在文档中的表格中列出了所有缩小匹配器,以及它们应用于哪些节点以及如何使用它们。以下是阅读该表格的方法
以及一些示例
m functionDecl(parameterCountIs(1))
m functionDecl(anyOf(isDefinition(), isVariadic()))
如您所见,**只允许一个缩小匹配器**,它位于节点匹配器的括号内。在第一个示例中,匹配器是 parameterCountIs
,在第二个示例中,它是 anyOf
。
在第二个示例中,我们使用单数 anyOf
匹配器来匹配多个其他缩小匹配器中的任何一个:isDefinition
或 isVariadic
。其他两个常见的组合缩小匹配器是 allOf()
和 unless()
。
如果您需要指定缩小匹配器(因为它是一些其他匹配器的必需参数),您可以使用 anything()
缩小匹配器来获得一个无操作的缩小匹配器。
遍历匹配器¶
遍历匹配器也可以被认为是形容词——至少大多数情况下是这样。它们也描述一个特定的节点,但与缩小匹配器的区别在于,描述的范围比单个节点更广。缩小匹配器说明了节点本身的一些信息(例如,它具有的参数数量),而遍历匹配器说明了节点的内容或在程序中的位置。
同样,文档是探索和理解这些匹配器的最佳位置,但这里有一个遍历匹配器 hasArraySize()
的简单示例
Given:
class MyClass { };
MyClass *p1 = new MyClass[10];
cxxNewExpr()
matches the expression 'new MyClass[10]'.
cxxNewExpr(hasArraySize(integerLiteral(equals(9))))
does not match anything
cxxNewExpr(hasArraySize(integerLiteral(equals(10))))
matches the expression 'new MyClass[10]'.
迭代匹配器开发示例¶
在开发匹配器时,如果您执行以下操作,将会更容易
写下您想要匹配的代码。尽可能以不同的方式写出来。例如:对于代码中的一些值,使用变量、常量和返回值的函数。将您想要匹配的代码放在函数内部、条件语句内部、函数调用内部以及内联函数定义内部。
写下您不想要匹配的代码,但看起来像是您想要的代码。写下良性的函数调用、良性的赋值等。
迭代您的匹配器,并将其视为您正在编写的_代码_。缩进它,将其复制到其他地方以防您的浏览器崩溃,甚至将其粘贴到一个很小的临时版本控制文件中。
作为上述内容的一个示例,下面是一个更复杂的匹配器的示例迭代开发过程。
**目标**:匹配函数调用,其中一个参数是带有整数字面量的赋值表达式,但函数参数在函数定义中具有默认值。
int add1(int a, int b) { return a + b; }
int add2(int c, int d = 8) { return c + d; }
int main() {
int x, y, z;
add1(x, y); // <- No match, no assignment
add1(3 + 4, y); // <- No match, no assignment
add1(z = x, y); // <- No match, assignment, but not an integer literal
add1(z = 2, y); // <- No match, assignment, integer literal, but function parameter lacks default value
add2(3, z = 2); // <- Match
}
以下是迭代开发过程
//-------------------------------------
// Step 1: Find all the function calls
m callExpr()
// Matches all calls, as expected.
//-------------------------------------
// Step 2: Start refining based on the arguments to the call
m callExpr(forEachArgumentWithParam()))
// Error: forEachArgumentWithParam expects two parameters
//-------------------------------------
// Step 3: Figure out the syntax to matching all the calls with this new operator
m callExpr(
forEachArgumentWithParam(
anything(),
anything()
)
)
// Matches all calls, as expected
//-------------------------------------
// Step 4: Find the calls with a binary operator of any kind
m callExpr(
forEachArgumentWithParam(
binaryOperator(),
anything()
)
)
// Does not match the first call, but matches the others
//-------------------------------------
// Step 5: Limit the binary operator to assignments
m callExpr(
forEachArgumentWithParam(
binaryOperator(isAssignmentOperator()),
anything()
)
)
// Now matches the final three calls
//-------------------------------------
// Step 6: Starting to refine matching the right-hand of the assignment
m callExpr(
forEachArgumentWithParam(
binaryOperator(
allOf(
isAssignmentOperator(),
hasRHS()
)),
anything()
)
)
// Error, hasRHS expects a parameter
//-------------------------------------
// Step 7:
m callExpr(
forEachArgumentWithParam(
binaryOperator(
allOf(
isAssignmentOperator(),
hasRHS(anything())
)),
anything()
)
)
// Okay, back to matching the final three calls
//-------------------------------------
// Step 8: Refine to just integer literals
m callExpr(
forEachArgumentWithParam(
binaryOperator(
allOf(
isAssignmentOperator(),
hasRHS(integerLiteral())
)),
anything()
)
)
// Now we match the final two calls
//-------------------------------------
// Step 9: Apply a restriction to the parameter definition
m callExpr(
forEachArgumentWithParam(
binaryOperator(
allOf(
isAssignmentOperator(),
hasRHS(integerLiteral())
)),
hasDefaultArgument()
)
)
// Now we match the final call