Clang-Tidy, del 1: moderniser din kildekode ved hjælp af C++11 / C++14automatiseret refactoring af din kildekode ved hjælp af kraftig open source-værktøj

16.03.2017 Kevin Funk

FacebookkvidreLinkedIn  e-mail

denne blogserie introducerer clang-tidy-værktøjet fra Clang/LLVM-projektet og viser, hvordan du bruger det til automatisk at refactor C++ kildekode og integrere med dit build-system, samt hvordan du bruger værktøjet på andre platforme end Unices.

Motivation: glæden ved ældre kodebaser

C++11 tilføjede en betydelig mængde nye C++ – sprogfunktioner, som til dato stadig ikke bruges i deres fulde omfang. De mest visuelle er helt sikkert auto, override, Lambda-udtryk, rækkebaseret for, ensartet initialiseringssyntaks, du navngiver det. Mens C++11 nu allerede er flere år gammel, er der stadig masser af kodebaser, der ikke bruger nogen af de nye sprogfunktioner, det være sig ved politik fra ledelsen eller ved ren dovenskab for at undgå porteringsindsatsen fra udviklersiden. Clang-Tidy fra LLVM compiler infrastructure project er her for i det mindste at overvinde sidstnævnte for at tillade automatisk refactoring af din kildekode, så den bruger de nye sprogfunktioner.

brug tilsidesættelse, nogen som helst?

Hvad sker der nu, hvis du allerede opretholder en ret stor kodebase, som stadig er kompileret under C++03-tilstand, men du vil omfavne at bruge C++11 i fremtiden med alle de nyttige funktioner, den har? Du vil sandsynligvis have masser og masser af kode, der ligner dette:

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

indtil videre har du selvfølgelig altid implementeret den rigtige basisklassemetode, fordi du har testet din kode grundigt via enhedstest! Selvfølgelig gjorde du det. Således koden er fint, men du vil nu gerne gå videre. Du vil gerne tilføje override-specifikatoren til hver enkelt reimplementeret metode i din kodebase, men selvfølgelig uden at ansætte en praktikant, der går gennem kodelinjen for linjen og tilføjer dem manuelt.

bare for at understrege, at tilføjelse af override faktisk tjener et formål, har vi for nylig rettet en fejl i 3D, hvor vi ikke overbelastede den korrekte basisklassemetode. Med specifikatoren tilføjet tidligere, vi ville have bemærket det samme, efter genkompilering.

vi tager eksemplet med manglende tilsidesættelse yderligere for at forklare grundlæggende brug af clang-tidy.

Clang-Tidy til undsætning!

Clang-Tidy er et clang-baseret C++ linter værktøj, der giver en shell eksekverbar kaldet clang-tidy som det vigtigste indgangspunkt. Det er en udvidelig ramme til diagnosticering af typiske programmeringsfejl eller stilproblemer — generelt alt, hvad der kan opdages under statisk analyse af koden. Den reelle fordel ved værktøjet er, at det desuden giver mulighed for automatisk at refactor kildekoden ved at anvende rettelser, som hvert enkelt problem kan give. Det er stærkt plugin-baseret og leveres med et nyttigt sæt plugins ud af kassen, som vi skal diskutere i næste afsnit.

LLVM logo -- hjem af clang-tidy

Clang-Tidy er et værktøj udviklet og vedligeholdt af Clang/LLVM samfund.

opsætning

når du kører clang-tidy, er det normalt nemt at komme via din distributions pakkehåndtering. Det er lige så nemt at installere det som at køre følgende kommandoer:

% sudo apt-get install clang-tidy

vi vil diskutere installation af værktøjet på andre platforme end i et af de kommende blogindlæg.

Bemærk: Vi anbefaler dig altid at installere den nyeste version (i skrivende stund anbefales versionen baseret på Clang/LLVM 3.9), da antallet af tilgængelige plugins/checkers varierer meget fra version til version og vokser konstant.

introduktion

Bemærk: i dette blogindlæg blev clang-tidy – 3.9 brugt

en typisk påkaldelse af kommandolinjeværktøjet ser sådan ud:

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

udfører det som dette, vil værktøjet udskrive en masse advarsler og noter (hvis relevant) på nøjagtig samme måde som Clang/GCC giver diagnostik også.

Clang-Tidy er et nyttigt statisk analyseværktøj alene med masser af forskellige tilgængelige brikker, dette er dog ikke fokus for dette blogindlæg. Vi vil hellere udnytte værktøjets kraftfulde refactoring kapaciteter til at modernisere vores kildekode.

notering af tilgængelige checkers

kørsel af værktøjet uden nogen specifikke kommandolinjeparametre kører standardsættet af checkers aktiveret af værktøjet. Lad os kontrollere, hvilke andre brikker det har at tilbyde (ved at bestå –checks=’*’ for at se dem alle), og specifikt grep for dem med modernisering i deres navne. Disse brikker går ind for brug af moderne sprogkonstruktioner:

$ 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

imponerende liste over muligheder allerede, er det ikke? Clang-Tidy sender faktisk nogle interessante brikker ud af kassen (fra Clang/LLVM 3.9), hvor listen vokser konstant fra frigivelse til frigivelse.

navnene på brikkerne er stort set selvforklarende (f. eks. modernisere-Brug-auto vil omfavne ved hjælp af auto, hvor det er relevant), men hvis du vil undersøge, hvad hver af dem betyder, bedes du se listen over brikker på clang-tidy-hjemmesiden:

for at vise, hvordan værktøjet bruges, lad os fokusere på moderniserings-brug-Tilsidesæt kontrol, da det er den mest anvendelige og mest ukontroversielle kontrol.

Refactoring en enkelt fil

vores tilsidesættelse eksempel igen:

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

kører clang-tidy på eksemplet (denne gang med modernisere-brug-Tilsidesæt checker aktiveret):

% 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

okay. Så det bemærkede, at Derived::reimplementMe(int) tilsidesætter en basisklassemetode, men mangler override specifikatoren! Nu kunne vi tilføje det manuelt… eller bare lade værktøjet gøre det for os ved at passere-rette!

kører det på eksemplet (med modernisere-brug-Tilsidesæt checker & rettelse – dens aktiveret):

% 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 anvendte rettelsen og indsatte override efter metodedeklarationen i linje 5. Færdig!

et par noter

der er et par ting værd at nævne:

  • ikke alle brikker af klang-tidy faktisk bære rettelse-dens, men dem, der starter med modernisere alle gør.
  • du kan bruge rettelse-its fra flere brikker på samme tid (overvej-kontrol=’moderniser-brug-Tilsidesæt,moderniser-Brug-auto’ -rettelse)
  • Running clang-tidy påberåber sig den komplette Clang compiler frontend, og har derfor brug for lidt tid til at fuldføre
  • Refactoring resultater fra clang-tidy er helt nøjagtige, fordi det understøttes af en fuldt udbygget C++-parser

refactoring et komplet projekt (CMake-baseret)

indtil videre har vi kørt clang-tidy på en enkelt, enkeltstående fil kun. Hvad sker der, hvis du har en mere kompleks projektopsætning, med masser af filer, alle med brugerdefinerede kompileringsflag? Clang-tidy fungerer altid på en enkelt fil, eller rettere, oversættelsesenhed. Vi kan hjælpe værktøjet med at finde ud af de korrekte kompileringsflag for hver oversættelsesenhed, vi udarbejder i vores projekt. Den mest bekvemme måde at køre den på er med en kompileringskommandodatabase. CMake kan automatisk generere en, og en gang en compile_commands.json er på plads, og en fungerende version af clang-tidy er i sti hele kodebasen kan analyseres med run-clang-tidy.py script (normalt afsendt som en del af installationen). Hvis ikke kan du blot hente det her.

Bemærk: Det anbefales stærkt at bruge run-clang-tidy.py at køre clang-tidy på et helt projekt, da det kører værktøjet flere gange parallelt og sørger for, at samtidige henrettelser ikke forstyrrer hinanden (f.eks.

generering af en compile_commands.JSON fil

til generering af compile_commands.JSON-fil i et CMake-baseret projekt skal du bare køre:

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

brug script til at køre clang-tidy

nu til at køre værktøjet med standardkontrol på hver oversættelsesenhed i projektet, skal du blot påkalde run-clang-tidy script inde i mappen med compile_commands.JSON file:

% run-clang-tidy.py

som set før, vil dette ikke ændre noget hidtil, da vi har kørt clang-tidy med bare standardkontrollerne aktiveret. For at f. eks. køre modernisere-brug-tilsidesætte kontrol af alle oversættelser enheder og faktisk refactor al din kode, denne påkaldelse er nødvendig:

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

sådan. clang-tidy vil nu blive påberåbt på hver oversættelse enhed i dit projekt og vil tilføje tilsidesættelser hvor mangler. Parameteren-header-filter=’.* ‘sørger for Klang-tidy faktisk refactors kode i overskrifterne forbruges i oversættelsen enhed. Parameteren checks=’ -*,… ‘ sørger for, at alle standardkontroller er deaktiveret.

Bemærk, at rettelserne kun anvendes, når run-clang-tidy er færdig! Scriptet registrerer kun de ændringer, der skal udføres, og anvender dem alle på en gang i slutningen.

kører andre brikker

igen, modernisere-brug-tilsidesættelse er blot et eksempel, clang-tidy har masser af andre brikker, som er nyttige. En anden super nyttig er modernisere-brug-nullptr checker, som omdanner 0, eller f.eks. NULL literals til moderne C++11 nullptr version. For at refactor alle anvendelser af de gamle stil bogstaver i dit projekt, skal du blot køre:

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

det er normalt en god ide at udføre en checker efter den anden og begå mellemliggende resultater (tænk på “Port mod C++11 nullptr”, “Port mod C++11 tilsidesættelse”,…) i dit versionsstyringssystem.

nogle virkelige verden eksempler

jeg har personligt brugt clang-tidy på en masse forskellige projekter allerede, med positive resultater. Husk, dette værktøj har perfekt kendskab til din kode (som det er i virkeligheden ved hjælp af Clang compiler frontend) og dermed vil refactor din kode uden nogensinde at indføre brudt kode.

eksempler:

  • denne patch for eksempel porte alle KDE rammer biblioteker mod C++11 nullptr, ved at røre omkring 9000 forskellige kode steder
  • denne patch porte KDE marmor kode base til C++11 tilsidesætte, ved at røre omkring 2300 forskellige kode steder

konklusion

Clang-Tidy er en kraftfuld værktøj, der gør portering din arv kodebase mod c++11 et spørgsmål om at køre en one-liner. Den leveres med et stort sæt standardcheckere, og listen over yderligere vokser konstant. De modernisere-checkers kan bruges til at modernisere/refactor din kildekode til at bruge nye C++ sprog funktioner.

i den næste del af denne serie vil vi diskutere, hvordan man bruger clang-tidy projektomfattende med andre byggesystemer.

brug for hjælp?

KDAB beskæftiger flere ingeniører, der arbejder med Clang Tooling dagligt. Vi hjælper dig gerne, hvis du har problemer med at bruge Clang Tooling i dit projekt eller ønsker at få mest muligt ud af det: ved at lade os implementere projektspecifikke kodekontroller eller køre automatiske refactoringværktøjer på din kodebase. Vi har hjulpet klienter med at modernisere deres meget store kodebaser med succes ved hjælp af værktøj – noget, som ikke ville have været muligt for dem omkostningsmæssigt at gøre det manuelt.

Kontakt os

FacebookkvidreLinkedIn  e-mail

kategorier: C++ / funktionel sikkerhed / sådan / Kdab Blogs / Kdab på spørgsmål / værktøj

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

Kevin Funk Senior ingeniør

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.