Clang-Tidy, partie 1 : Modernisez votre code source à l’aide de C++11 / C++14 refactorisation automatique de votre code source à l’aide de puissants outils open-source

16.03.2017 Kevin Funk

 Facebook  Twitter  LinkedIn  Email

Cette série de blogs présentera l’utilitaire clang-tidy du projet Clang / LLVM et montrera comment l’utiliser pour refactoriser automatiquement le code source C++ et l’intégrer à votre système de construction, ainsi que comment utiliser l’outil sur d’autres plates-formes que Unices.

Motivation: La joie des bases de code héritées

C++11 a ajouté une quantité importante de nouvelles fonctionnalités du langage C ++ qui, à ce jour, ne sont toujours pas utilisées dans leur pleine mesure. Les plus visuels sont à coup sûr auto, override, les expressions Lambda, la plage for, la syntaxe d’initialisation uniforme, vous l’appelez. Alors que C ++ 11 a déjà plusieurs années, il y a encore beaucoup de bases de code qui n’utilisent aucune des nouvelles fonctionnalités du langage, que ce soit par politique de la direction ou par pure paresse pour éviter l’effort de portage du côté développeur. Clang-Tidy du projet d’infrastructure du compilateur LLVM est là pour au moins surmonter ce dernier, pour permettre la refactorisation automatique de votre code source afin qu’il utilise les nouvelles fonctionnalités du langage.

Utilisez override, n’importe qui?

Maintenant, que se passe-t-il si vous maintenez déjà une base de code assez importante qui se compile toujours en mode C ++ 03 mais que vous souhaitez utiliser C++ 11 à l’avenir, avec toutes les fonctionnalités utiles dont il dispose? Vous aurez probablement beaucoup de code similaire à celui-ci:

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

Jusqu’à présent, vous avez bien sûr toujours ré-implémenté la méthode de classe de base correcte, car vous avez largement testé votre code via des tests unitaires! Bien sûr que tu l’as fait. Ainsi, le code va bien mais vous souhaitez maintenant passer à autre chose. Vous souhaitez ajouter le spécificateur override à chaque méthode réimplantée dans votre base de code, mais bien sûr sans embaucher un stagiaire qui parcourt le code ligne par ligne et les ajoute manuellement.

Juste pour souligner que l’ajout de override sert effectivement un but, nous venons de corriger un bug dans Qt 3D où nous ne surchargions pas la méthode de classe de base correcte. Avec le spécificateur ajouté plus tôt, nous l’aurions remarqué instantanément, après la recompilation.

Nous allons prendre l’exemple missing-override plus loin pour expliquer l’utilisation de base de clang-tidy.

Clang-Tidy à la rescousse!

Clang-Tidy est un outil de linter C++ basé sur clang qui fournit un exécutable shell appelé clang-tidy comme point d’entrée principal. C’est un cadre extensible pour diagnostiquer les erreurs de programmation typiques, ou les problèmes de style — généralement tout ce qui peut être détecté lors de l’analyse statique du code. Le véritable avantage de l’outil est qu’il permet en outre de refactoriser automatiquement le code source en appliquant des correctifs que chaque problème individuel peut fournir. Il est fortement basé sur des plugins et est livré avec un ensemble utile de plugins prêts à l’emploi, dont nous allons discuter dans le paragraphe suivant.

 Logo LLVM - accueil de clang-tidy

Clang-Tidy est un outil développé et maintenu par la communauté Clang/LLVM.

Setup

Lorsque vous utilisez Linux, clang-tidy est généralement facile à obtenir via le gestionnaire de paquets de votre distribution. Sur Ubuntu Linux par exemple, l’installer est aussi simple que d’exécuter les commandes suivantes:

% sudo apt-get install clang-tidy

Nous discuterons de l’installation de l’outil sur d’autres plates-formes que Linux dans l’un des prochains articles de blog.

Remarque: Nous vous recommandons de toujours installer la dernière version (au moment de la rédaction, la version basée sur Clang/LLVM 3.9 est recommandée), car le nombre de plugins / checkers disponibles varie considérablement d’une version à l’autre et augmente constamment.

Introduction

Remarque: Dans cet article de blog, clang-tidy-3.9 a été utilisé

Une invocation typique de l’outil de ligne de commande ressemble à ceci:

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

En l’exécutant comme ceci, l’outil imprimera un tas d’avertissements et de notes (le cas échéant), exactement de la même manière que Clang / GCC fournit également des diagnostics.

Clang-Tidy est un outil d’analyse statique utile à lui seul avec de nombreux vérificateurs disponibles, ce n’est cependant pas l’objet de cet article de blog. Nous préférons tirer parti des puissantes capacités de refactorisation de l’outil pour moderniser notre code source.

La liste des vérificateurs disponibles

L’exécution de l’outil sans paramètres de ligne de commande spécifiques exécutera l’ensemble de vérificateurs par défaut activé par l’utilitaire. Vérifions quels autres pions il a à offrir (en passant -checks = ‘*’ pour les voir tous), et spécifiquement grep pour ceux avec modernize dans leurs noms. Ces dames préconisent l’utilisation de constructions de langage moderne:

$ 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

Liste impressionnante d’options déjà, n’est-ce pas? Clang-Tidy expédie en effet des pions intéressants hors de la boîte (à partir de Clang / LLVM 3.9), la liste augmentant constamment d’une version à l’autre.

Les noms des dames sont à peu près explicites (par exemple, modernize-use-auto adoptera l’utilisation automatique le cas échéant), mais si vous souhaitez explorer ce que chacun d’eux signifie, veuillez consulter la liste des dames sur la page d’accueil de clang-tidy:

Pour montrer comment l’outil est utilisé, concentrons-nous sur le vérificateur modernize-use-override, car il s’agit du vérificateur le plus applicable et le plus non controversé.

Refactorisation d’un seul fichier

Notre exemple de remplacement à nouveau:

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

Exécution de clang-tidy sur l’exemple (cette fois avec le vérificateur modernize-use-override activé):

% 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

D’accord. Il a donc remarqué que Derived::reimplementMe(int) remplace une méthode de classe de base mais manque du spécificateur override ! Maintenant, nous pourrions l’ajouter manuellement… ou simplement laisser l’outil le faire pour nous en passant – fix!

L’exécuter sur l’exemple (avec le vérificateur modernize-use-override & fix – il est activé):

% 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 a appliqué le fix-it et a inséré override après la déclaration de méthode à la ligne 5. C’est fait !

Quelques notes

Il y a quelques choses à mentionner:

  • Tous les dames de clang-tidy ne portent pas de correctifs, mais celles qui commencent par moderniser le font toutes.
  • Vous pouvez utiliser fix-its à partir de plusieurs vérificateurs en même temps (consider-checks= ‘modernize-use-override, modernize-use-auto’-fix)
  • L’exécution de clang-tidy appelle le frontend complet du compilateur Clang, il faudra donc un certain temps pour terminer
  • Les résultats de refactorisation de clang-tidy sont parfaitement précis, car ils sont soutenus par un analyseur C++ à part entière

Refactorisation d’un projet complet (basé sur CMake)

Jusqu’à présent, nous avons exécuté clang-tidy sur un seul fichier autonome uniquement. Que se passe-t-il si vous avez une configuration de projet plus complexe, avec beaucoup de fichiers, tous avec des indicateurs de compilation personnalisés? Clang-tidy fonctionne toujours sur un seul fichier, ou plutôt sur une unité de traduction. Nous pouvons aider l’outil à déterminer les indicateurs de compilation corrects pour chaque unité de traduction que nous compilons dans notre projet. Le moyen le plus pratique de l’exécuter est d’utiliser une base de données de commandes de compilation. CMake peut générer automatiquement un, et une fois un compile_commands.json est en place et une version de travail de clang-tidy est dans le CHEMIN la base de code entière peut être analysée avec run-clang-tidy.script py (généralement livré dans le cadre de l’installation). Sinon, vous pouvez simplement le télécharger ici.

Remarque: Il est fortement recommandé d’utiliser run-clang-tidy.py pour exécuter clang-tidy sur un projet entier, car il exécutera l’outil plusieurs fois en parallèle et s’assurera que les exécutions simultanées n’interfèrent pas les unes avec les autres (par exemple en évitant de modifier un en-tête partagé en parallèle et en générant du code cassé).

Génération d’un compile_commands.fichier json

Pour générer les compile_commands.fichier json dans un projet basé sur CMake, exécutez simplement:

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

Utilisez le script pour exécuter clang-tidy

Maintenant, pour exécuter l’outil avec les vérifications par défaut sur chaque unité de traduction du projet, appelez simplement le script run-clang-tidy dans le répertoire avec les commandes compile_commands.fichier json:

% run-clang-tidy.py

Comme vu précédemment, cela ne modifiera rien jusqu’à présent, car nous avons exécuté clang-tidy avec uniquement les vérifications par défaut activées. Par exemple, pour exécuter la vérification modernize-use-override sur toutes les unités de traduction et refactoriser réellement tout votre code, cette invocation est nécessaire:

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

C’est tout. clang-tidy sera désormais invoqué sur chaque unité de traduction de votre projet et ajoutera des remplacements lorsqu’ils sont manquants. Le paramètre-header-filter=’.*’ s’assure que clang-tidy refactorise réellement le code dans les en-têtes consommés dans l’unité de traduction. Le paramètre checks=’-*,…’ s’assure que toutes les vérifications par défaut sont désactivées.

Notez que les correctifs ne sont appliqués qu’une fois run-clang-tidy terminé! Le script n’enregistrera que les modifications à effectuer et les appliquera toutes en même temps à la fin.

Exécution d’autres dames

Encore une fois, le modernize-use-override n’est qu’un exemple, clang-tidy a beaucoup d’autres dames qui sont utiles. Un autre très utile est le vérificateur modernize-use-nullptr, qui transforme 0, ou par exemple NULL littéraux en version moderne C++11 nullptr. Pour refactoriser toutes les utilisations des littéraux à l’ancienne dans votre projet, exécutez simplement:

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

C’est généralement une bonne idée d’effectuer un vérificateur après l’autre, en validant des résultats intermédiaires (pensez à « Port vers C++11 nullptr », « Port vers C++11 override », into) dans votre système de contrôle de version.

Quelques exemples du monde réel

J’ai personnellement utilisé clang-tidy sur beaucoup de projets différents déjà, avec des résultats positifs. N’oubliez pas que cet outil a une parfaite connaissance de votre code (car il utilise en fait le frontend du compilateur Clang) et refactorisera ainsi votre code sans jamais introduire de code cassé.

Exemples:

  • Ce patch met par exemple en communication toutes les bibliothèques de Frameworks de KDE vers C++11 nullptr, en touchant environ 9000 emplacements de code différents
  • Ce patch met en communication la base de code KDE Marble vers C++11 override, en touchant environ 2300 emplacements de code différents

Conclusion

Clang-Tidy est un outil puissant ce qui rend le portage de votre base de code héritée vers C++ 11 une question d’exécution d’une seule ligne. Il est livré avec un grand ensemble de dames par défaut et la liste des autres s’allonge constamment. Les modernize-checkers peuvent être utilisés pour moderniser / refactoriser votre code source pour utiliser les nouvelles fonctionnalités du langage C++.

Dans la prochaine partie de cette série, nous discuterons de la façon d’utiliser clang-tidy à l’échelle du projet avec d’autres systèmes de construction.

Besoin d’aide?

KDAB emploie plusieurs ingénieurs qui travaillent quotidiennement avec l’outillage Clang. Nous sommes heureux de vous aider si vous rencontrez des problèmes d’utilisation de l’outillage Clang dans votre projet ou si vous souhaitez en tirer le meilleur parti: en nous laissant implémenter des vérifications de code spécifiques au projet ou exécuter des outils de refactorisation automatique sur votre base de code. Nous avons aidé les clients à moderniser leurs bases de code très volumineuses avec succès en utilisant des outils – ce qui n’aurait pas été possible pour eux de le faire manuellement en termes de coûts.

Contactez-nous

 Facebook  Twitter  LinkedIn  Courriel

Catégories : C++ / Sécurité fonctionnelle / Mode d’emploi / Blogs KDAB / KDAB sur Qt / Outillage

Mots clés : refactoring automatisé / C++ / C++11 / C++14 / Clang / LLVM

Kevin Funk Ingénieur logiciel principal

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.