Clang-Tidy, deel 1: modernisering van uw broncode met behulp van C++11 / C++14automatische refactoring van uw broncode met behulp van Krachtige Open-source tooling

16.03.2017 Kevin Funk

FacebookTwitterLinkedIn e-mail

deze blog serie introduceert het Clang-tidy hulpprogramma van het Clang / LLVM project en laat zien hoe het te gebruiken om automatisch refactor C++ broncode en te integreren met uw build-systeem, evenals hoe de tool te gebruiken op andere platforms dan Unices.

motivatie: the joy of legacy code bases

C++11 voegde een aanzienlijke hoeveelheid nieuwe C++ – taalfuncties toe die tot op heden nog niet volledig worden gebruikt. De meest visuele zijn zeker auto, override, Lambda expressions, range-based for, uniform initialisatie syntaxis, noem maar op. Terwijl C++11 is nu enkele jaren oud al, Er is nog steeds veel code bases die geen gebruik maken van een van de nieuwe taal features, of het nu door beleid van het management, of door pure luiheid om de porting inspanning van de ontwikkelaar kant te voorkomen. Clang-Tidy van het LLVM compiler infrastructure project is hier om op zijn minst de laatste te overwinnen, om automatische refactoring van uw broncode mogelijk te maken, zodat het de nieuwe taalfuncties gebruikt.

overschrijven gebruiken, iemand?

nu, wat gebeurt er als u al een vrij grote code base die nog steeds compileert onder C++03 mode, maar je wilt omarmen met behulp van C++11 in de toekomst, met alle handige functies die het heeft? U zult waarschijnlijk veel en veel code vergelijkbaar met deze:

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

tot nu toe heb je natuurlijk altijd opnieuw de juiste base-class methode geà mplementeerd, want je hebt je code uitgebreid getest via unit tests! Natuurlijk deed je dat. Dus de code is prima, maar je zou nu graag verder gaan. U wilt de override specifier toevoegen aan elke her-geà mplementeerde methode in uw code base, maar natuurlijk zonder het inhuren van een stagiair die gaat door de code regel-voor-regel en voegt ze handmatig.

om te benadrukken dat het toevoegen van override inderdaad een doel dient, hebben we onlangs een bug in Qt 3D opgelost waarbij we de juiste basisklasse methode niet overbelastten. Met de specifier eerder toegevoegd, zouden we direct hebben gemerkt, na hercompilatie.

we nemen het voorbeeld missing-override verder om het basisgebruik van clang-tidy uit te leggen.

Clang-Tidy te hulp!

Clang-Tidy is een Clang-gebaseerde C++ linter tool die een shell uitvoerbaar genaamd clang-tidy als het belangrijkste toegangspunt biedt. Het is een uitbreidbaar raamwerk voor het diagnosticeren van typische programmeerfouten, of stijlproblemen — over het algemeen alles wat kan worden gedetecteerd tijdens statische analyse van de code. Het echte voordeel van de tool is dat het bovendien maakt het mogelijk om automatisch refactor de broncode door het toepassen van fixits elk afzonderlijk probleem kan bieden. Het is zwaar plugin-gebaseerd en wordt geleverd met een nuttige set van plugins uit de doos, die we gaan bespreken in de volgende paragraaf.

LLVM logo -- home of clang-tidy

Clang-Tidy is een tool ontwikkeld en onderhouden door de Clang/LLVM gemeenschap.

Setup

wanneer Linux wordt uitgevoerd, is clang-tidy meestal eenvoudig te verkrijgen via de pakketbeheerder van uw distributie. Op Ubuntu Linux bijvoorbeeld, het installeren is net zo eenvoudig als het uitvoeren van de volgende opdrachten:

% sudo apt-get install clang-tidy

We zullen bespreken het installeren van de tool op andere platforms dan Linux in een van de komende blog posts.

Noot: Wij raden u aan om altijd de nieuwste versie te installeren (op het moment van schrijven wordt de versie op basis van Clang/LLVM 3.9 aanbevolen), omdat het aantal beschikbare plugins/checkers sterk varieert van versie tot versie en voortdurend groeit.

Inleiding

opmerking: in deze blogpost werd clang-tidy-3.9 gebruikt

een typische aanroep van het opdrachtregelprogramma ziet er als volgt uit:

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

door het zo uit te voeren, zal de tool een hoop waarschuwingen en notities afdrukken (indien van toepassing), op precies dezelfde manier Clang/GCC diagnostiek bieden, ook.

Clang-Tidy is een nuttig statisch analysetool op zichzelf met veel verschillende beschikbare checkers, dit is echter niet de focus van deze blogpost. We willen liever gebruik maken van de krachtige refactoring mogelijkheden van de tool om onze broncode te moderniseren.

beschikbare checkers

het uitvoeren van het gereedschap zonder specifieke opdrachtregelparameters zal de standaard set van checkers uitvoeren die door het hulpprogramma zijn ingeschakeld. Laten we eens kijken wat andere checkers het te bieden heeft (door het passeren van –checks=’*’ om ze allemaal te zien), en specifiek grep voor degenen met moderniseren in hun naam. Die checkers pleiten voor het gebruik van moderne taalconstructies:

$ 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

indrukwekkende lijst van opties al, is het niet? Clang-Tidy schepen inderdaad een aantal interessante checkers uit de doos (vanaf Clang/LLVM 3.9), met de lijst groeit voortdurend van release tot release.

de namen van de schijven zijn vrijwel vanzelfsprekend (bijvoorbeeld modernize-use-auto zal omarmen met behulp van auto waar van toepassing), maar als u wilt verkennen wat elk van hen betekent, raadpleeg dan de lijst met schijven op de Clang-tidy homepage:

om te laten zien hoe het gereedschap wordt gebruikt, laten we ons richten op de modernize-use-override checker, omdat het de meest toepasselijke en meest oncontroversiale checker is.

Refactoring van een enkel bestand

ons overschrijvingsvoorbeeld opnieuw:

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

Clang-tidy draaien op het voorbeeld (deze keer met de modernize-use-override checker ingeschakeld):

% 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

Oké. Het merkte dus op dat Derived::reimplementMe(int) een basisklasse methode overschrijft, maar de override specifier ontbreekt! Nu kunnen we dat handmatig toevoegen … of gewoon laat de tool doen voor ons door het passeren-fix!

draaien op het voorbeeld (met modernize-use-override checker & fix-its ingeschakeld):

% 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 paste de fix-it toe en voegde override toe na de methodedeclaratie in regel 5. Klaar!

enkele noten

er zijn een paar dingen die het vermelden waard zijn:

  • niet alle checkers van clang-tidy eigenlijk dragen fix-its, maar degenen die beginnen met moderniseren allemaal doen.
  • U kunt gebruik maken van fix-zijn van meerdere dammen op hetzelfde moment (denk -controles=’moderniseren-gebruik-overschrijven,moderniseren-gebruik-auto’ -fix)
  • Uitvoeren clang-netjes roept de volledige Clang compiler frontend, dus het zal wat tijd nodig hebben om te voltooien
  • Refactoring resultaten van clang-netjes zijn perfect nauwkeurig, vanwege het feit dat het is gemaakt door een volwaardige C++ parser

Refactoring een compleet project (CMake-gebaseerd)

tot nu toe hebben we uitvoeren clang-netjes op een enkel, op zichzelf staand bestand. Wat gebeurt er als je een complexer project setup hebt, met veel bestanden, allemaal met aangepaste compile flags? Clang-tidy werkt altijd op één enkel dossier, of beter gezegd, vertaaleenheid. We kunnen de tool helpen om de juiste compilatievlaggen te achterhalen voor elke vertaaleenheid die we in ons project compileren. De meest handige manier om het uit te voeren is met een compile command database. CMake kan er automatisch een genereren, en zodra een compile_commands.json is op zijn plaats en een werkende versie van clang-tidy is in PAD de volledige code basis kan worden geanalyseerd met de run-clang-tidy.py script (meestal verzonden als onderdeel van de installatie). Zo niet, dan kunt u het hier gewoon downloaden.

Opmerking: Het wordt ten zeerste aanbevolen om run-clang-tidy.py om clang-tidy op een heel project uit te voeren, omdat het de tool meerdere keren parallel draait en ervoor zorgt dat gelijktijdige uitvoeringen elkaar niet storen (bijvoorbeeld door het vermijden van het wijzigen van een gedeelde header parallel en op zijn beurt het genereren van gebroken code).

genereren van een compile_commands.json-bestand

voor het genereren van de compile_commands.json-bestand in een cmake-gebaseerd project, voer gewoon uit:

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

gebruik het script om clang-tidy

uit te voeren voor het uitvoeren van de tool met de standaard controles op elke vertaaleenheid in het project, kunt u eenvoudig het script run-clang-tidy in de map aanroepen met de compile_commands.json-bestand:

% run-clang-tidy.py

zoals eerder gezien, zal dit tot nu toe niets wijzigen, omdat we clang-tidy hebben uitgevoerd met alleen de standaardcontroles ingeschakeld. Om bijvoorbeeld de modernize-use-override controle uit te voeren op alle translations units en eigenlijk al uw code te refactor, is deze aanroep nodig:

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

dat is het. clang-tidy zal nu worden aangeroepen op elke vertaaleenheid in uw project en zal overschrijvingen toevoegen waar deze ontbreken. De parameter-header-filter=”.* ‘zorgt ervoor dat clang-tidy eigenlijk refactors code in de headers wordt verbruikt in de vertaaleenheid. De parameter checks=’ -*,… ‘ zorgt ervoor dat alle standaard controles uitgeschakeld zijn.

merk op dat de fixes alleen worden toegepast als run-clang-tidy klaar is! Het script registreert alleen de uit te voeren wijzigingen en past ze allemaal in één keer toe aan het einde.

andere checkers draaien

de modernize-use-override is slechts een voorbeeld, clang-tidy heeft veel andere checkers die nuttig zijn. Een andere handige is de modernize-use-nullptr checker, die 0, of bijvoorbeeld NULL literals omzet in moderne C++11 nullptr versie. Om alle toepassingen van de oude stijl literals in uw project refactor, gewoon uitvoeren:

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

het is meestal een goed idee om de ene checker na de andere uit te voeren, door tussentijdse resultaten (denk aan “Port towards C++11 nullptr”, “Port towards C++11 override”, …) in je versiebeheersysteem te committen.

enkele voorbeelden uit de echte wereld

persoonlijk heb ik clang-tidy al gebruikt op een heleboel verschillende projecten, met positieve resultaten. Vergeet niet, deze tool heeft een perfecte kennis van uw code (zoals het is in feite met behulp van de Clang compiler frontend) en dus zal uw code refactor zonder ooit de invoering van gebroken code.

voorbeelden:

  • deze patch ports bijvoorbeeld alle KDE Frameworks bibliotheken naar C++11 nullptr, door het aanraken van ongeveer 9000 verschillende code locaties
  • deze patch ports de KDE Marble code base naar C++11 override, door het aanraken van ongeveer 2300 verschillende code locaties

conclusie

Clang-Tidy is een krachtige tool die maakt het porten van uw oude code base naar C++11 een kwestie van het uitvoeren van een one-liner. Het wordt geleverd met een grote set van standaard checkers en de lijst met extra degenen groeit voortdurend. De modernize-checkers kunnen worden gebruikt om uw broncode te moderniseren/refactor om nieuwe C++ – taalfuncties te gebruiken.

In het volgende deel van deze serie zullen we bespreken hoe clang-tidy project-breed te gebruiken met andere bouwsystemen.

hulp nodig?

KDAB heeft verschillende ingenieurs in dienst die dagelijks met Clang Tooling werken. We zijn blij om u te helpen in het geval u problemen met het gebruik van Clang Tooling in uw project of wilt u het meeste uit te halen: door ons te laten implementeren project-specifieke code controles of uitvoeren van automatische refactoring tools op uw code base. We hebben geholpen klanten moderniseren hun zeer grote code bases met succes met behulp van tooling-iets dat niet haalbaar zou zijn geweest voor hen kosten-wise handmatig doen.

Contact met ons op

FacebookTwitterLinkedIne-Mail

Categorieën: C++ / Functionele Veiligheid / het / KDAB Blogs / KDAB op Qt / Gereedschappen

Tags: geautomatiseerde refactoring / C++ / C++11 / C++14 / Clang / LLVM

Kevin Funk Senior Software Engineer

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.