自动检测相同的连续std :: string :: find()调用

orb*_*boy 12 c++ regex pcre static-code-analysis cppcheck

在代码审查期间,我找到了这样的源代码:

void f_odd(std::string &className, std::string &testName)
{
   if (className.find("::") != std::string::npos)
   {
     testName = className.substr(className.find("::") + 2);
     (void)className.erase(className.find("::"), std::string::npos);
   }
}
Run Code Online (Sandbox Code Playgroud)

在这个函数中,std :: string :: find()被调用三次,具有相同的模式(这里是"::").

这个代码当然可以重构为

void f(std::string &className, std::string &testName)
{
   const size_t endOfClassNamePos = className.find("::");
   if (endOfClassNamePos != std::string::npos)
   {
     testName = className.substr(endOfClassNamePos + 2);
     (void)className.erase(endOfClassNamePos, std::string::npos);
   }
}
Run Code Online (Sandbox Code Playgroud)

find只被调用一次.

有人知道检测这种模式的策略吗?我有一个巨大的代码库,我打算发现这种模式.我计划使用Windows或Linux环境.

潜在的策略

  1. 使用/调整静态代码分析工具,如cppcheck来检测这些奇怪的东西.
  2. 使用正则表达式在代码库中搜索.
  3. 使用/ adapt clang-tidy来检测这种模式.
  4. 用某种语言编写自定义检查器(例如Python)来检测这些问题.在这种情况下,应该对预处理的代码执行检查.

没有Go

  • 手动审查

更新1

我决定从潜在的策略开始1).我打算让cppcheck适应这个问题.

Cppcheck提供了基于PCRE正则表达式编写自定义规则的可能性.为此,必须使用启用的PCRE支持编译cppcheck.由于当前测试环境是基于Linux的,因此可以使用以下命令下载最新版本的cppcheck:

git clone https://github.com/danmar/cppcheck.git && cd cppcheck

之后,编译并安装该工具,如下所示:

sudo make install HAVE_RULES=yes

现在基本工具设置完成了.为了开发一个cppcheck规则,我准备了一个简单的测试用例(file:test.cpp),类似于本文第一部分中的示例代码.此文件包含三个功能和cppcheck规则应在发出警告f_oddf_odd1大约相同的连续std::string::find通话.

TEST.CPP:

#include <string>
void f(std::string &className, std::string &testName)
{
   const size_t endOfClassNamePos = className.find("::");
   if (endOfClassNamePos != std::string::npos)
   {
     testName = className.substr(endOfClassNamePos + 2);
     (void)className.erase(endOfClassNamePos, std::string::npos);
   }
 }

 void f_odd(std::string &className, std::string &testName)
 {
    if (className.find("::") != std::string::npos)
    {
       testName = className.substr(className.find("::") + 2);
       (void)className.erase(className.find("::"), std::string::npos);
    }
 }

 #define A "::"
 #define B "::"
 #define C "::"
 void f_odd1(std::string &className, std::string &testName)
 {
   if (className.find(A) != std::string::npos)
   {
      testName = className.substr(className.find(B) + 2);
      (void)className.erase(className.find(C), std::string::npos);
   }
 }
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.现在必须调整cppcheck以捕获连续相同的std::string::find调用.为此我创建了一个cppcheck_rule文件,其中包含一个匹配连续相同std::string::find调用的正则表达式:

<?xml version="1.0"?>
<rule>
<tokenlist>normal</tokenlist>
<pattern><![CDATA[([a-zA-Z][a-zA-Z0-9]*)(\s*\.\s*find)(\s*\(\s*\"[ -~]*\"\s*\))[ -\{\n]*(\1\2\3)+[ -z\n]]]></pattern>
<message>
    <severity>style</severity>
    <summary>Found identical consecutive std::string::find calls.</summary>
</message>
Run Code Online (Sandbox Code Playgroud)

此文件可用于扩展有关新检查的cppcheck.我们试试吧:

cppcheck --rule-file=rules/rule.xml test/test.cpp

而输出是

Checking test/test.cpp...
[test/test.cpp:14]: (style) Found identical consecutive std::string::find calls.
[test/test.cpp:26]: (style) Found identical consecutive std::string::find calls.
Run Code Online (Sandbox Code Playgroud)

现在,std::string::find可以在C/C++代码中检测到相同的连续调用.有人知道更好/更有效或更聪明的解决方案吗?

参考文献:


MSa*_*ers 1

这种工具的主要问题是词法分析只能检查是否存在文本重复。以您的示例为例,className.find("::")如果变量两次引用同一字符串,则调用两次是一个潜在的问题。但让我对您的代码添加一个className = className.substr(className.find("::") + 2);更改: . 突然下一句的意思className.find发生了巨大的变化。

你能发现这样的变化吗?为此你需要一个成熟的编译器,即使这样你也必须持悲观态度。坚持你的例子,可以className通过迭代器改变吗?您需要注意的不仅仅是直接操纵。

难道就没有什么积极的消息吗?嗯:现有的编译器确实有类似的机制。它称为公共子表达式消除,从概念上讲,它的工作方式与您希望它在上面的示例中工作的方式相同。但这在某种程度上也是坏消息:如果这种情况是可检测到的,那么它并不重要,因为它已经被编译器优化了!