编写特定的 Clang-tidy 检查以避免将表达式传递到 std::vector::reserve()

Writing a specific Clang-tidy check to avoid passing an expression into std::vector::reserve()

在我们的代码库中,我们总是使用 std::vector::reserve() 来获得更高的性能。大多数时候他们工作得很好。

此外,有时我们可能会将表达式传递给 reserve() 函数。 例如,

const double length = GetLength();
const double step = GetStep();
v.reserve(static_cast<std::size_t>(length / step));

但是,由于一些算法错误,length可能是negative number,如果没有运行程序,几乎不可能知道这个问题。每次出现这个问题,都会报错然后崩溃是不可避免的:

terminate called after throwing an instance of 'std::length_error'
terminate called recursively
  what():  vector::reserve

幸运的是,我们现在正在使用 clang-tidy,所以我想知道我是否可以应用一些自定义规则,例如 avoid passing an expression into std::vector::reserve() function 并手动检查每个变量是否可能溢出。

有没有办法编写 clang-tidy 规则来执行此操作?

首先,您必须准确决定要报告的内容。为简单起见,我将您的问题解释为想要报告对 std::vector::reserve 的任何调用,其中参数不是 identifier.

接下来,任何clang-tidy检查的核心是AST matcher expression. The tool clang-query can be used to directly run an AST matcher against a given input program, so I'll use that to show such a matcher. (Incorporating that matcher into a clang-tidy check is then done the like any other, as described in the documentation。)

所以,这是一个命令 运行s clang-query 在文件 reserve1.cc 上,并报告对 std::vector::reserve 的所有调用,其参数不是标识符:

clang-query -c='m
  callExpr(
    callee(
      cxxMethodDecl(
        matchesName("std::vector<.*>::reserve")
      )),
    unless(
      hasArgument(0,
        expr(declRefExpr())
      ))
  )' \
  reserve1.cc --

匹配器表达式相当 self-explanatory 除了 declRefExpr,它是匹配标识符的匹配器,Clang 称之为“声明引用”。

当以上命令为运行时输入如下:

// reserve1.cc
// Example for a check that the 'reserve' argument is "simple".
// 

#include <vector>

double GetLength();
double GetStep();
void bad()
{
  const double length = GetLength();
  const double step = GetStep();
  std::vector<int> v;
  v.reserve(static_cast<std::size_t>(length / step));   // reported
}

void good(std::size_t len)
{
  std::vector<int> v;
  v.reserve(len);                                       // not reported
}

struct Other {
  void reserve(int);
};
void irrelevant(Other o, int x)
{
  o.reserve(x + x);                                     // not reported
}

// EOF

它打印:

Match #1:

/home/scott/wrk/learn/clang/reserve-argument/reserve1.cc:14:3: note: "root"
      binds here
  v.reserve(static_cast<std::size_t>(length / step));   // reported
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 match.

我建议花一些时间在您感兴趣的程序上使用 clang-query 交互式测试和调整 AST 匹配器表达式,然后再进行将其嵌入到 clang-tidy.

的额外工作

我的 this answer 中有更多关于 clang-query 的信息,可以回答一个模糊的类似问题。