编写特定的 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
的信息,可以回答一个模糊的类似问题。
在我们的代码库中,我们总是使用 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
的信息,可以回答一个模糊的类似问题。