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

标签 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);
   }
}

在此函数中,std::string::find() 以相同的模式调用了 3 次(此处为“::”)。

这段代码当然可以重构为

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);
   }
}

find 只被调用一次。

问题

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

潜在策略

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

不要走

  • 人工审核

更新 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-rule,我准备了一个简单的测试用例(文件:test.cpp),类似于本文第一部分的示例代码。该文件包含三个函数,cppcheck-rule 应在 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);
   }
 }

到目前为止一切顺利。现在必须调整 cppcheck 以捕获连续相同的 std::string::find 调用。为此,我创建了一个 cppcheck_rule-file包含匹配连续相同 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>

此文件可用于扩展 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.

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

引用资料:


最佳答案

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

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

没有正面消息吗?好吧:现有的编译器确实有类似的机制。它被称为通用子表达式消除,它在概念上的工作方式与您希望它在上面的示例中工作一样。但这在某种程度上也是一个坏消息:如果情况是可以检测到的,那并不重要,因为它已经被编译器优化了!

关于c++ - 自动检测相同的连续 std::string::find() 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36889420/

相关文章:

c++ - 在 fastcgi 应用程序中访问环境变量

正则表达式:递归反向引用 - 有何用处?

regex - 如何在字母数字字符和非字母数字字符之间添加空格?

regex - MongoDB 计数和正则表达式搜索计数不匹配

c++ - 链接器错误 LNK2038 : mismatch detected in Release mode

c++ - 这些 C++ 函数返回什么? ClassA*& func1()

c++ - 如何解析简单的 C++ typedef 指令?

java - 以编程方式导入 C/C++ 项目 eclipse

regex - 用于基于正则表达式的计数的 splunk 查询

r - 仅在顶层拆分带有嵌套括号的字符串,其中 "level"由括号确定