高级检查功能¶
此页面介绍了改进和扩展已添加到 build/clang-plugin 中的检查的其他方法。
添加测试¶
毫无疑问,您已经在 build/clang-plugin/tests 中看到了现有检查的测试。添加测试非常简单;并且您的评审人员应该坚持要求您这样做。只需复制任何测试的现有格式以及诊断如何标记为预期即可。
一个需要注意的地方——所有 clang 插件检查都应用于所有测试。我们尝试编写测试,以便只有一个检查应用于它。如果您编写了一个在现有测试上触发的检查,请尝试稍微修复现有测试,以便新检查不会在它上面触发。
使用 Bind 输出更多有用的信息¶
您可能一直在想知道 .bind()
到底是什么。您一直在到处看到它,但从未真正解释过它的用途以及何时使用它。
.bind()
用于为通过匹配器发现的 AST 的一部分命名,以便以后可以使用它。让我们回到我们的示例匹配器
AstMatcher->addMatcher(
traverse(TK_IgnoreUnlessSpelledInSource,
ifStmt(allOf(
has(
binaryOperator(
has(
declRefExpr(hasType(enumDecl()))
)
)
),
hasElse(
ifStmt(allOf(
unless(hasElse(anything())),
has(
binaryOperator(
has(
declRefExpr(hasType(enumDecl()))
)
)
)
))
)
))
.bind("node")),
this);
现在 .bind("node")
更有意义了。我们正在命名匹配的 If 语句,以便我们可以在以后调用 Result.Nodes.getNodeAs<IfStmt>("node")
时引用它。
假设我们想在警告消息中提供枚举的类型。在我们的匹配器中最终会看到两个枚举——第一个 if 语句中的枚举和第二个中的枚举。我们将任意选择第一个并将其命名为 enumType
AstMatcher->addMatcher(
traverse(TK_IgnoreUnlessSpelledInSource,
ifStmt(allOf(
has(
binaryOperator(
has(
declRefExpr(hasType(enumDecl().bind("enumType")))
)
)
),
hasElse(
ifStmt(allOf(
unless(hasElse(anything())),
has(
binaryOperator(
has(
declRefExpr(hasType(enumDecl()))
)
)
)
))
)
))
.bind("node")),
this);
在我们的 check() 函数中,我们可以像这样使用它
void MissingElseInEnumComparisons::check(
const MatchFinder::MatchResult &Result) {
const auto *MatchedDecl = Result.Nodes.getNodeAs<IfStmt>("node");
const auto *EnumType = Result.Nodes.getNodeAs<EnumDecl>("enumType");
diag(MatchedDecl->getIfLoc(),
"Enum comparisons to %0 in an if/else if block without a trailing else.",
DiagnosticIDs::Warning) << EnumType->getName();
}
重复匹配器调用¶
如果您发现自己在多个地方重复使用相同的几个匹配器,您可以将其转换为变量以使用。
auto isTemporaryLifetimeBoundCall =
cxxMemberCallExpr(
onImplicitObjectArgument(anyOf(has(cxxTemporaryObjectExpr()),
has(materializeTemporaryExpr()))),
callee(functionDecl(isMozTemporaryLifetimeBound())));
auto hasTemporaryLifetimeBoundCall =
anyOf(isTemporaryLifetimeBoundCall,
conditionalOperator(
anyOf(hasFalseExpression(isTemporaryLifetimeBoundCall),
hasTrueExpression(isTemporaryLifetimeBoundCall))));
上面的示例是无参数的,但如果您需要提供一个更改的参数,您可以将其转换为 lambda
auto hasConstCharPtrParam = [](const unsigned int Position) {
return hasParameter(
Position, hasType(hasCanonicalType(pointsTo(asString("const char")))));
};
auto hasParamOfType = [](const unsigned int Position, const char *Name) {
return hasParameter(Position, hasType(asString(Name)));
};
auto hasIntegerParam = [](const unsigned int Position) {
return hasParameter(Position, hasType(isInteger()));
};
AstMatcher->addMatcher(
callExpr(
hasName("fopen"),
hasConstCharPtrParam(0))
.bind("funcCall"),
this);
允许列出现有调用站点¶
虽然这不是一个很好的情况,但如果您需要,可以设置一个现有调用站点的允许列表。一个简单的允许列表在 NoGetPrincipalURI 中进行了演示。 NoNewThreadsChecker 是一个更复杂的方法来设置更大的允许列表的示例。
自定义注释¶
可以创建自定义注释,这些注释在编译时将不起作用,但可供静态分析检查使用。这些可以用于注释特殊类型的源和接收器(例如)。我们目前在树中有一些此类示例(例如 MOZ_CAN_RUN_SCRIPT
),但目前在此文档中没有关于如何设置和使用它们的详细演练。(欢迎提交补丁。)