2016-04-27 15 views
12

Während eines Code-Review, fand ich Quellcode wie folgt aus:automatisch identisch aufeinander folgenden std :: string erkennen :: find() ruft

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

Innerhalb dieser Funktion std :: string :: find() wird dreimal mit dem gleichen Muster aufgerufen (hier "::").

Dieser Code kann natürlich

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

wo find aufgerufen wird nur einmal

Refactoring werden.

Frage

Kennt jemand eine Strategie wie dieses ein solches Muster auf das Erkennen? Ich habe eine riesige Codebasis, wo ich dieses Muster erkennen möchte. Ich möchte eine Windows- oder Linux-Umgebung verwenden.

Mögliche Strategien

  1. Verwendung/Anpassung eines statischen Code-Analyse-Tool, wie cppcheck diese Art von Seltsamkeiten zu erkennen.
  2. In der Code-Basis mit regulärem Ausdruck suchen.
  3. Verwenden Sie/clang-ordentlich anpassen, um dieses Muster zu erkennen.
  4. Schreiben Sie eine benutzerdefinierte Überprüfung in einer Sprache (z. B. Python), die diese Probleme erkennt. In diesem Fall sollte die Überprüfung auf vorverarbeitetem Code durchgeführt werden.

No Gos

  • Handbuch Bewertung

Update 1

entschied ich mich mit potentieller Strategie 1) zu starten. Ich plane, cppcheck anzupassen, um dieses Problem zu finden.

Cppcheck bietet die Möglichkeit, benutzerdefinierte Regeln basierend auf regulären PCRE-Ausdrücken zu schreiben. Dazu muss cppcheck mit aktivierter PCRE-Unterstützung kompiliert werden. Da die aktuelle Testumgebung auf Linux-Basis, können die folgenden Befehle, um die neueste Version von cppcheck zum Download verwendet werden:

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

Danach, kompilieren und installieren Sie das Tool wie folgt:

sudo make install HAVE_RULES=yes

Jetzt ist das grundlegende Tool Setup abgeschlossen. Um eine cppcheck-Regel zu entwickeln, habe ich einen einfachen Testfall (Datei: test.cpp) erstellt, ähnlich dem Beispielcode im ersten Abschnitt dieses Artikels. Diese Datei enthält drei Funktionen und die cppcheck-Regel soll unter f_odd und f_odd1 über aufeinanderfolgende identische std::string::find Aufrufe eine Warnung ausgeben.

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

So weit so gut. Jetzt muss cppcheck optimiert werden, um aufeinanderfolgende identische std::string::find Aufrufe zu erhalten. Dazu habe ich eine cppcheck_rule-file geschaffen, die einen regulären Ausdruck enthält, die aufeinanderfolgende identische std::string::find Anrufe übereinstimmt:

<?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> 

Diese Datei verwendet werden kann cppcheck über einen neuen Scheck zu erweitern. Lets try:

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

und der Ausgang ist

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. 

nun identische aufeinanderfolgende std::string::find Anrufe können in C/C++ Codes detektiert werden. Kennt jemand eine bessere/effizientere oder schlauere Lösung? Referenzen

:


+3

Sie könnten Ihre eigenen [clang-ordentlich] (http://clang.llvm.org/extra/clang-tidy/) schreiben, um dies zu erkennen. – Jonas

+0

@Jonas Danke clang-Tidy ist ein weiteres potentia Werkzeug, das den Job machen könnte. Ich werde meinen Bereich für mögliche Lösungen aktualisieren. – orbitcowboy

+2

Können Sie Ihre Frage so umformulieren, dass sie nicht wie eine Anforderung von Toolempfehlungen aussieht? Diese sind [explizit außerhalb des Themas] (https://stackoverflow.com/help/on-topic) auf dieser Site. – 5gon12eder

Antwort

1

Das Hauptproblem bei einem solchen Werkzeug ist, dass eine lexikalische Analyse kann nur überprüfen, ob es Text ist Wiederholung. In diesem Beispiel ist ein zweimaliger Aufruf von className.find("::") ein potenzielles Problem, wenn die Variable zweimal auf dieselbe Zeichenfolge verweist. Aber lassen Sie mich eine kleine Änderung an Ihrem Code hinzufügen: className = className.substr(className.find("::") + 2);. Plötzlich hat sich die Bedeutung des nächsten className.finddramatisch geändert.

Können Sie solche Änderungen finden? Sie brauchen dafür einen vollwertigen Compiler, und selbst dann müssen Sie pessimistisch sein. Bleiben Sie bei Ihrem Beispiel, könnte className über einen Iterator geändert werden? Es ist nicht nur direkte Manipulation, die Sie beachten müssen.

Gibt es keine positiven Nachrichten? Nun, existierende Compiler haben einen ähnlichen Mechanismus. Es heißt Common Subexpression Elimination und funktioniert konzeptionell so, wie es im obigen Beispiel funktionieren soll. Aber das ist auch eine schlechte Nachricht: Wenn die Situation feststellbar ist, ist das unwichtig, weil sie vom Compiler bereits optimiert wurde!