高级检查功能

此页面介绍了改进和扩展已添加到 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),但目前在此文档中没有关于如何设置和使用它们的详细演练。(欢迎提交补丁。)