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

时间:2016-04-27 12:10:19

标签: 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()被调用三次,具有相同的模式(这里是“::”)。

此代码当然可以重构为

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

其中只调用一次。

问题

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

潜在策略

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

    • 手动审核

    更新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-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次调用。有人知道更好/更有效或更聪明的解决方案吗?

    参考文献:

1 个答案:

答案 0 :(得分:1)

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

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

没有正面消息吗?那么:现有的编译器确实有类似的机制。它被称为Common Subexpression Elimination,它在概念上起作用,就像你希望它在上面的例子中一样。但这也是一个坏消息:如果情况可以检测到,它就不重要了,因为它已经被编译器优化了!