Clang-Tidy, parte 1: Modernizzare il codice sorgente utilizzando C++11/C++14 refactoring automatico del codice sorgente utilizzando potenti strumenti open-source

16.03.Facebook  Twitter  LinkedIn  Email

Questa serie di blog introdurrà l’utilità clang-tidy dal progetto Clang / LLVM e mostrerà come usarlo per refactoring automaticamente il codice sorgente C++ e integrarsi con il sistema di compilazione, nonché come utilizzare lo strumento su altre piattaforme diverse da Unifes.

Motivazione: La gioia delle basi di codice legacy

C++11 ha aggiunto una quantità significativa di nuove funzionalità del linguaggio C++ che fino ad oggi non sono ancora utilizzate nella loro piena estensione. I più visivi sono di sicuro auto, override, espressioni Lambda, for basato sull’intervallo, sintassi di inizializzazione uniforme, è il nome. Mentre C++11 ha già diversi anni, ci sono ancora molte basi di codice che non usano nessuna delle nuove funzionalità del linguaggio, sia per politica di gestione, sia per pura pigrizia per evitare lo sforzo di porting da parte dello sviluppatore. Clang-Tidy dal progetto di infrastruttura del compilatore LLVM è qui per superare almeno quest’ultimo, per consentire il refactoring automatico del codice sorgente in modo da utilizzare le nuove funzionalità del linguaggio.

Usa override, chiunque?

Ora, cosa succede se si mantiene già una base di codice abbastanza grande che è ancora compilata in modalità C++03 ma si vuole abbracciare l’utilizzo di C++11 in futuro, con tutte le funzioni utili che ha? Probabilmente avrai un sacco di codice simile a questo:

struct Base { virtual void reimplementMe(int a) {}};struct Derived : public Base { virtual void reimplementMe(int a) {}};

Finora ovviamente hai sempre ri-implementato il metodo di classe base corretto, perché hai ampiamente testato il tuo codice tramite test unitari! Certo che l’hai fatto. Quindi il codice va bene, ma ora vorresti andare avanti. Vuoi aggiungere l’identificatore override a ogni singolo metodo ri-implementato nella tua base di codice, ma ovviamente senza assumere un tirocinante che passa attraverso il codice riga per riga e li aggiunge manualmente.

Giusto per sottolineare che l’aggiunta di override ha davvero uno scopo, abbiamo appena corretto un bug in Qt 3D in cui non stavamo sovraccaricando il metodo corretto della classe base. Con lo specificatore aggiunto in precedenza, avremmo notato immediatamente, dopo la ricompilazione.

Prenderemo l’esempio missing-override ulteriormente per spiegare l’uso di base di clang-tidy.

Clang-Tidy in soccorso!

Clang-Tidy è uno strumento di linter C++ basato su clang che fornisce un eseguibile di shell chiamato clang-tidy come punto di ingresso principale. È un framework estensibile per diagnosticare errori di programmazione tipici o problemi di stile, generalmente tutto ciò che può essere rilevato durante l’analisi statica del codice. Il vero vantaggio dello strumento è che consente inoltre di refactoring automaticamente il codice sorgente applicando fixits ogni singolo problema può fornire. È fortemente basato su plugin e viene fornito con un utile set di plugin pronti all’uso, di cui parleremo nel prossimo paragrafo.

LLVM logo home home di clang-tidy

Clang-Tidy è uno strumento sviluppato e gestito dalla comunità Clang/LLVM.

Setup

Quando si esegue Linux, clang-tidy è di solito facile da ottenere tramite il gestore di pacchetti della distribuzione. Su Ubuntu Linux, ad esempio, installarlo è facile come eseguire i seguenti comandi:

% sudo apt-get install clang-tidy

Discuteremo l’installazione dello strumento su altre piattaforme rispetto a Linux in uno dei prossimi post del blog.

Nota: Ti consigliamo di installare sempre l’ultima versione (al momento della scrittura, si consiglia la versione basata su Clang/LLVM 3.9), poiché il numero di plugin/checkers disponibili varia notevolmente da versione a versione e cresce costantemente.

Introduzione

Nota: In questo post del blog, clang-in ordine-3.9 è stato utilizzato

Una tipica invocazione dello strumento della riga di comando simile a questo:

% clang-tidy test.cpp -- -Imy_project/include -DMY_DEFINES ...

Eseguendo come questo, lo strumento di stampa in un mucchio di avvertenze e le note (se applicabile), esattamente allo stesso modo Clang/GCC fornire diagnostica, troppo.

Clang-Tidy è un utile strumento di analisi statica da solo con un sacco di diverse pedine disponibili, questo, tuttavia, non è al centro di questo post del blog. Preferiamo sfruttare le potenti funzionalità di refactoring dello strumento per modernizzare il nostro codice sorgente.

Elenco delle pedine disponibili

L’esecuzione dello strumento senza parametri specifici della riga di comando eseguirà il set predefinito di pedine abilitato dall’utilità. Controlliamo quali altre pedine ha da offrire (passando-checks= ‘ * ‘ per vederle tutte), e in particolare grep per quelli con modernize nei loro nomi. Quelle pedine sostengono l’uso di costrutti linguistici moderni:

$ clang-tidy --list-checks -checks='*' | grep "modernize" modernize-avoid-bind modernize-deprecated-headers modernize-loop-convert modernize-make-shared modernize-make-unique modernize-pass-by-value modernize-raw-string-literal modernize-redundant-void-arg modernize-replace-auto-ptr modernize-shrink-to-fit modernize-use-auto modernize-use-bool-literals modernize-use-default modernize-use-emplace modernize-use-nullptr modernize-use-override modernize-use-using

Impressionante lista di opzioni già, non è vero? Clang-Tidy spedisce infatti alcune pedine interessanti fuori dalla scatola (a partire da Clang/LLVM 3.9), con la lista che cresce costantemente da un rilascio all’altro.

I nomi delle pedine sono praticamente auto-esplicativi (ad esempio modernize-use-auto abbraccerà l’uso di auto dove applicabile), ma se vuoi esplorare cosa significa ciascuno di essi, consulta l’elenco delle pedine sulla homepage di clang-tidy:

Per mostrare come viene utilizzato lo strumento, concentriamoci sul checker modernize-use-override, in quanto è il checker più applicabile e più incontrovertibile.

Refactoring di un singolo file

Di nuovo il nostro esempio di override:

struct Base { virtual void reimplementMe(int a) {}};struct Derived : public Base { virtual void reimplementMe(int a) {}};

Esecuzione di clang-tidy sull’esempio (questa volta con il controllo modernize-use-override abilitato):

% clang-tidy-3.9 -checks='modernize-use-override' test.cpp -- -std=c++111 warning generated./home/kfunk/test.cpp:5:18: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' virtual void reimplementMe(int a) {} ^ override

Va bene. Quindi ha notato che Derived::reimplementMe(int) sovrascrive un metodo di classe base ma manca lo specificatore override! Ora potremmo aggiungerlo manualmente manually o semplicemente lasciare che lo strumento lo faccia per noi passando-fix!

Eseguendolo sull’esempio (con modernize-use-override checker & fix-its abilitato):

% clang-tidy-3.9 -checks='modernize-use-override' -fix test.cpp -- -std=c++111 warning generated./home/kfunk/test.cpp:5:18: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' virtual void reimplementMe(int a) {} ^ override/home/kfunk/test.cpp:5:5: note: FIX-IT applied suggested code changes virtual void reimplementMe(int a) {} ^/home/kfunk/test.cpp:5:38: note: FIX-IT applied suggested code changes virtual void reimplementMe(int a) {} ^clang-tidy applied 2 of 2 suggested fixes.

Clang-tidy ha applicato il fix-it e ha inserito override dopo la dichiarazione del metodo nella riga 5. Fatto!

Un paio di note

Ci sono alcune cose degne di nota:

  • Non tutte le pedine di clang-tidy in realtà portano fix-its, ma quelle che iniziano con modernize lo fanno tutti.
  • È possibile utilizzare fix-la sua più pedine allo stesso tempo (si pensi, controlli=’modernizzare-utilizzo-sostituzione a modernizzare-utilizzare-auto-fix)
  • Esecuzione di clang-in ordine richiama la completa compilatore Clang frontend, quindi avrà bisogno di un po’ di tempo
  • Refactoring risultati da clang-in ordine sono perfettamente accurata, a causa del fatto che è supportato da un vero e proprio parser C++

Refactoring di un progetto completo (CMake-based)

finora abbiamo eseguito clang-ordinata in un unico file autonomo solo. Cosa succede se si dispone di una configurazione del progetto più complessa, con molti file, tutti con flag di compilazione personalizzati? Clang-tidy opera sempre su un singolo file, o meglio, unità di traduzione. Possiamo aiutare lo strumento a capire i flag di compilazione corretti per ogni unità di traduzione che compiliamo nel nostro progetto. Il modo più conveniente per eseguirlo è con un database di comandi di compilazione. CMake può generare automaticamente uno e una volta compile_commands.json è a posto e una versione funzionante di clang-tidy è in PERCORSO l’intera base di codice può essere analizzata con run-clang-tidy.script py (di solito spedito come parte dell’installazione). Se non si può semplicemente scaricarlo qui.

Nota: Si consiglia vivamente di utilizzare run-clang-tidy.py per eseguire clang-tidy su un intero progetto, poiché eseguirà lo strumento più volte in parallelo e si assicurerà che le esecuzioni simultanee non interferiscano l’una con l’altra (ad esempio evitando di modificare un’intestazione condivisa in parallelo e a sua volta generando codice interrotto).

Generazione di un compile_commands.file json

Per generare i compile_commands.file json in un progetto basato su CMake, basta eseguire:

% cd my-cmake-based-project% mkdir build% cd build% cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..

Usa script per eseguire clang-tidy

Ora per eseguire lo strumento con i controlli predefiniti su ogni unità di traduzione nel progetto, è sufficiente richiamare lo script run-clang-tidy all’interno della directory con i compile_commands.file json:

% run-clang-tidy.py

Come visto prima, questo non modificherà nulla finora, dato che abbiamo eseguito clang-tidy con solo i controlli predefiniti abilitati. Per esempio eseguire il controllo modernize-use-override su tutte le unità di traduzione e in realtà refactoring di tutto il codice, è necessaria questa invocazione:

% run-clang-tidy.py -header-filter='.*' -checks='-*,modernize-use-override' -fix

Ecco fatto. clang-tidy verrà ora richiamato su ogni unità di traduzione nel progetto e aggiungerà le sostituzioni dove manca. Il parametro-header-filter=’.* ‘fa in modo clang-tidy effettivamente refactoring codice nelle intestazioni di essere consumato nell’unità di traduzione. Il parametro checks=’ -*,… ‘ assicura che tutti i controlli predefiniti siano disabilitati.

Si noti che le correzioni vengono applicate solo quando run-clang-tidy è terminato! Lo script registrerà solo le modifiche da eseguire e le applicherà tutte contemporaneamente alla fine.

Esecuzione di altre pedine

Ancora una volta, modernize-use-override è solo un esempio, clang-tidy ha molte altre pedine che sono utili. Un altro super utile è il controllo modernize-use-nullptr, che trasforma 0, o ad esempio NULL letterali nella versione moderna C++11 nullptr. Per refactoring tutti gli usi dei letterali vecchio stile nel progetto, è sufficiente eseguire:

% run-clang-tidy.py -header-filter='.*' -checks='-*,modernize-use-nullptr' -fix

Di solito è una buona idea eseguire un checker dopo l’altro, commettendo risultati intermedi (pensa a “Porta verso C++11 nullptr”, “Porta verso C++11 override”, …) nel tuo sistema di controllo della versione.

Alcuni esempi del mondo reale

Ho già usato personalmente clang-tidy su molti progetti diversi, con risultati positivi. Ricorda, questo strumento ha una perfetta conoscenza del tuo codice (in quanto utilizza il frontend del compilatore Clang) e quindi refactoring il tuo codice senza mai introdurre codice rotto.

Esempi:

  • Questa patch, per esempio, tutte le porte di KDE Frameworks librerie in direzione di C++11 nullptr, toccando circa 9000 diverse posizioni di codice
  • Questa patch porte KDE Marmo base di codice di C++11 ignorare, toccando circa 2300 diverse posizioni di codice

Conclusione

Clang-Tidy è un potente strumento che rende il porting di codice legacy base verso il C++11 è una questione di esecuzione di un one-liner. Viene fornito con un grande set di pedine predefinite e l’elenco di quelli aggiuntivi cresce costantemente. I modernize-checkers possono essere utilizzati per modernizzare / refactoring il codice sorgente per utilizzare nuove funzionalità del linguaggio C++.

Nella prossima parte di questa serie discuteremo come utilizzare clang-tidy a livello di progetto con altri sistemi di costruzione.

Hai bisogno di aiuto?

KDAB impiega diversi ingegneri che lavorano quotidianamente con Clang Tooling. Siamo lieti di assisterti nel caso in cui tu abbia problemi con gli strumenti Clang nel tuo progetto o desideri ottenere il massimo da esso: permettendoci di implementare controlli di codice specifici del progetto o eseguire strumenti di refactoring automatico sulla tua base di codice. Abbiamo aiutato i clienti a modernizzare le loro basi di codice molto grandi usando con successo gli utensili-qualcosa che non sarebbe stato fattibile per loro in termini di costi farlo manualmente.

contattaci

FacebookTwitterLinkedIne-Mail

Categorie: C++ / Sicurezza Funzionale / Come / KDAB Blog / KDAB su Qt / Utensileria

Tag: automatizzato di refactoring / C++ / C++11 / C++14 / Clang / LLVM

Kevin Funk Senior Software Engineer

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.