添加检查

在使用 clang-query 完成匹配器后,是时候将其提升到下一步,将其转换为 C++ 并将其运行在整个 m-c 代码库上,看看会发生什么。

Clang 插件位于 build/clang-plugin,在这里我们将介绍添加插件所需的步骤。要了解最近一次检查是如何添加的,您可以查看 Checks.inc 的日志,这是需要编辑的文件之一。这也是我们接下来要介绍的内容。

添加新检查的样板步骤

首先选择一个名称。选择一个有意义的名称,不使用标点符号,最多 8 个单词左右。在本例中,我们将它命名为“枚举比较缺少 Else”。

  1. 在 build/clang-plugin/Checks.inc、ChecksIncludes.inc 和 moz.build 中按字母顺序添加它。

  2. cd build/clang-plugin && touch MissingElseInEnumComparisons.h MissingElseInEnumComparisons.cpp

  3. 复制现有简单 .h 文件(例如 build/clang-plugin/ScopeChecker.h)的内容,并编辑类名和头文件保护。

  4. 为您的实现创建以下样板代码

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "MissingElseInEnumComparisons.h"
#include "CustomMatchers.h"

void MissingElseInEnumComparisons::registerMatchers(MatchFinder *AstMatcher) {

}

void MissingElseInEnumComparisons::check(const MatchFinder::MatchResult &Result) {

}

将您的匹配器转换为 C++

在完成样板代码后,现在我们可以专注于将匹配器转换为 C++。一旦它在 C++ 中,您还可以利用一些技巧,使您的匹配器更容易阅读和理解。

转换匹配器的要点是采用以下伪代码,并将您的整个匹配器粘贴到“foo”所在的位置;保留 .bind(“node”)

AstMatcher->addMatcher(
    traverse(TK_IgnoreUnlessSpelledInSource,
        foo
        .bind("node")),
    this);

老实说,通常就这么简单。这是一个有效的示例,我直接从 Compiler Explorer 粘贴过来的

AstMatcher->addMatcher(
  traverse(TK_IgnoreUnlessSpelledInSource,
    ifStmt(allOf(
            has(
                 binaryOperator(
                     has(
                         declRefExpr(hasType(enumDecl().bind("enum")))
                     )
                 )
             ),
             hasElse(
                 ifStmt(allOf(
                    unless(hasElse(anything())),
                    has(
                         binaryOperator(
                             has(
                                 declRefExpr(hasType(enumDecl()))
                             )
                         )
                     )
                 ))
            )
         ))
        .bind("node")),
    this);

如果由于某种原因您没有使用 IgnoreUnlessSpelledInSource 遍历模式,请删除对 traverse 的调用以及相应的右括号。(此外,如果您将此代码与现有源代码进行比较,请注意,由于此遍历模式是 clang 的新功能,大多数历史 clang 检查都没有使用它。)

连接警告和错误

要开始使用一些简单的输出,只需获取此处的样板警告并将其粘贴进去即可

const auto *MatchedDecl = Result.Nodes.getNodeAs<IfStmt>("node");
  diag(MatchedDecl->getIfLoc(),
      "Enum comparisons in an if/else if block without a trailing else.",
      DiagnosticIDs::Warning);

您需要修改两件事

  1. 确保“node”与您在上面 .bind() 中输入的内容匹配。

  2. getNodeAs<IfStmt> 需要更改为“node”的元素类型。在上面,我们将“node”绑定到一个 IfStmt,所以我们需要将其转换为该类型。如果错误地执行此步骤,会导致 clang 在编译期间崩溃,就像出现了一些内部编译器错误一样。

在 Central 上运行

接下来,需要在您的 .mozconfig 中添加 ac_add_options --enable-clang-plugin 并进行构建。您的插件将自动编译并在整个代码库中使用。我建议使用 ./mach build | tee output.txt,然后 grep "Enum comparisons" output.txt | cut -d " " -f 3- | sort | uniq。(cut 用于去除行中的时间戳。)