2016-04-27 61 views
12

在代碼審查,我發現源代碼是這樣的:自動檢測相同的連續的std :: string :: find()方法調用

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. 使用/修改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調用的警告。

測試。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. 

現在,相同的連續std::string::find呼叫可以在C/C++代碼來檢測。有人知道更好/更有效或更聰明的解決方案嗎?

參考文獻:


+3

你可以編寫你自己的[clang-tidy](http://clang.llvm.org/extra/clang-tidy/)檢查來檢測這個。 – Jonas

+0

@Jonas謝謝clang-tidy是另一個可以完成這項工作的潛力工具。我會更新我的潛在解決方案部分。 – orbitcowboy

+2

您是否可以重新說明您的問題,使其不會顯示爲對工具建議的請求?這些網站上的[明確脫離主題](https://stackoverflow.com/help/on-topic)。 – 5gon12eder

回答

1

用這種工具的主要問題是,如果有文本重複一個詞法分析只能檢查。以你爲例,兩次調用className.find("::")是一個潛在的問題,如果變量引用兩次相同的字符串。但讓我添加一個更改爲您的代碼:className = className.substr(className.find("::") + 2);。突然,下一個className.find的含義已經大大改變了

你能找到這樣的改變嗎?你需要一個完整的編譯器,即使這樣你也必須保持悲觀。堅持你的榜樣,可以通過迭代器更改className嗎?這不僅僅是你需要意識到的直接操縱。

有沒有利好消息?那麼:現有的編譯器確實有類似的機制。它被稱爲Common Subexpression Elimination,它在概念上工作,就像你希望它在上面的例子中工作一樣。但這也是一個壞消息:如果情況是可以檢測到的,那就不重要了,因爲它已經被編譯器優化了!

相關問題