2016-04-27 34 views
12

Durante una revisione del codice, ho trovato il codice sorgente in questo modo:Rileva automaticamente identico std :: string consecutivo :: find() chiama

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

All'interno di questa funzione, std :: string :: find() è chiamato tre volte con lo stesso modello (qui "::").

Questo codice può naturalmente essere riscritta per

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

dove ritrovamento viene chiamato solo una volta.

Domanda

Qualcuno sa una strategia per il rilevamento di un tale modello come questo? Sto avendo una base di codice enorme, in cui ho intenzione di individuare questo modello. Ho intenzione di utilizzare un ambiente Windows o Linux.

possibili strategie

  1. Usare/adattare uno strumento di analisi statica del codice, come cppcheck per rilevare questo tipo di stranezze.
  2. Cerca all'interno del code base con espressione regolare.
  3. Utilizzare/Adattare Clang-Tidy per il rilevamento di questo modello.
  4. Scrivere un correttore personalizzato in una lingua (ad es. Python) che rilevi questi problemi. In questo caso, il controllo dovrebbe essere eseguito su codice preelaborato.

di No Go

  • recensione Manuale

Update 1

ho deciso di iniziare con un potenziale strategia di 1). Ho intenzione di adattare cppcheck per catturare questo problema.

Cppcheck offre la possibilità di scrivere regole personalizzate, basate su espressioni regolari PCRE. Per questo, cppcheck deve essere compilato con il supporto PCRE abilitato. Dal momento che l'ambiente di test corrente è basato-Linux, i seguenti comandi possono essere utilizzati per scaricare l'ultima versione di cppcheck:

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

Dopo di che, compilare e installare lo strumento come segue:

sudo make install HAVE_RULES=yes

Ora la configurazione dello strumento di base è terminata. Per sviluppare una regola cppcheck, ho preparato un semplice caso di test (file: test.cpp), simile al codice di esempio nella prima sezione di questo articolo. Questo file contiene tre funzioni e la regola cppcheck deve emettere un avviso su f_odd e f_odd1 su chiamate identiche consecutive da 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); 
    } 
} 

Fin qui tutto bene. Ora cppcheck deve essere ottimizzato per intercettare le chiamate identiche consecutive da std::string::find. Per questo ho creato un cppcheck_rule-file che contiene un'espressione regolare che corrisponde consecutivi identici std::string::find chiamate:

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

Questo file può essere utilizzato per estendere cppcheck su un nuovo controllo. proviamo:

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

e l'uscita è

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. 

Ora, identici consecutivi std::string::find chiamate possono essere rilevati in codici C/C++. Qualcuno conosce una soluzione migliore/più efficiente o più intelligente?

Riferimenti:


+3

È possibile scrivere il proprio controllo [clang-ordine] (http://clang.llvm.org/extra/clang-tidy/) per rilevare questo. – Jonas

+0

@Jonas Grazie a clang-tidy è un altro potente strumento che potrebbe fare il lavoro. Aggiornerò la mia potenziale sezione di soluzioni. – orbitcowboy

+2

Puoi ripetere la tua domanda in modo che non sembri una richiesta di suggerimenti sugli strumenti? Questi sono [esplicitamente off-topic] (https://stackoverflow.com/help/on-topic) su questo sito. – 5gon12eder

risposta

1

Il problema principale di un tale strumento è che un'analisi lessicale può verificare solo se c'è testuale ripetizione. Prendendo il tuo esempio, chiamare il numero className.find("::") due volte è un potenziale problema se la variabile fa riferimento alla stessa stringa due volte. Ma lasciatemi aggiungere small al codice: className = className.substr(className.find("::") + 2);. All'improvviso il significato del prossimo className.find è stato modificato a drammaticamente.

Potete trovare tali cambiamenti? Hai bisogno di un compilatore completo per questo, e anche allora devi essere pessimista. Attenendosi al tuo esempio, è possibile modificare className tramite un iteratore? Non è solo la manipolazione diretta di cui devi essere consapevole.

Non ci sono notizie positive? Bene: i compilatori esistenti hanno un meccanismo simile. Si chiama Common Subexpression Elimination e funziona concettualmente come vorreste che funzioni nell'esempio sopra. Ma questa è anche una cattiva notizia in un modo: se la situazione è rilevabile, non è importante perché è già ottimizzata dal compilatore!