Clang-Tidy, del 1: modernisera din källkod med C++11 / C++14automatiserad refactoring av din källkod med hjälp av kraftfulla verktyg med öppen källkod

16.03.2017 Kevin Funk

FacebookTwitter LinkedIne-post

denna bloggserie kommer att introducera clang-tidy-verktyget från Clang/LLVM-projektet och visa hur man använder det för att automatiskt refactor C++ källkod och integrera med ditt byggsystem, samt hur man använder verktyget på andra plattformar än Unices.

Motivation: glädjen av äldre kodbaser

C++11 lade till en betydande mängd nya C++ – språkfunktioner som hittills fortfarande inte används i sin fulla utsträckning. De mest visuella är säkert auto, override, Lambda-uttryck, intervallbaserad for, enhetlig initialiseringssyntax, du heter det. Medan C++11 Nu är flera år gammal, finns det fortfarande många kodbaser som inte använder någon av de nya språkfunktionerna, vare sig det är av policy från ledningen eller av ren latskap för att undvika portningsinsatsen från utvecklarsidan. Clang-Tidy från LLVM-kompilatorinfrastrukturprojektet är här för att åtminstone övervinna det senare, för att möjliggöra automatisk refactoring av din källkod så att den använder de nya språkfunktionerna.

använd åsidosättning, någon?

nu, vad händer om du redan har en ganska stor kodbas som fortfarande kompilerar under C++03-läge men du vill omfamna med C++11 i framtiden, med alla användbara funktioner som den har? Du kommer sannolikt att ha massor av kod som liknar detta:

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

hittills har du naturligtvis alltid implementerat rätt basklassmetod, eftersom du har testat din kod i stor utsträckning via enhetstester! Självklart gjorde du det. Således är koden bra men du vill nu gå vidare. Du vill lägga till override specificeraren till varje enskild re-implementerad metod i din kodbas, men naturligtvis utan att anställa en praktikant som går igenom koden rad för rad och lägger till dem manuellt.

bara för att betona att lägga till override verkligen tjänar ett syfte, har vi nyligen Fixat ett fel i Qt 3d där vi inte överbelastade rätt basklassmetod. Med specifieraren tillagd tidigare skulle vi ha märkt omedelbart efter omkompilering.

vi tar exemplet missing-override vidare för att förklara grundläggande användning av clang-tidy.

Clang-snyggt till undsättning!

Clang-Tidy är en clang-baserade C++ linter verktyg som ger ett skal körbar kallas clang-tidy som den viktigaste inkörsport. Det är en utbyggbar ram för att diagnostisera typiska programmeringsfel eller stilproblem — i allmänhet allt som kan upptäckas under statisk analys av koden. Den verkliga fördelen med verktyget är att det dessutom gör det möjligt att automatiskt refactor källkoden genom att tillämpa fixits varje enskild fråga kan ge. Det är starkt plugin-baserat och kommer med en användbar uppsättning plugins ur lådan, som vi kommer att diskutera i nästa stycke.

LLVM logo-hem för clang-tidy

Clang-Tidy är ett verktyg som utvecklats och underhålls av Clang/LLVM samhället.

Setup

när du kör Linux är clang-tidy vanligtvis lätt att få via din Distributions pakethanterare. På Ubuntu Linux till exempel, installera det är lika enkelt som att köra följande kommandon:

% sudo apt-get install clang-tidy

vi diskuterar att installera verktyget på andra plattformar än Linux i ett av de kommande blogginläggen.

notera: Vi rekommenderar att du alltid installerar den senaste versionen (i skrivande stund rekommenderas versionen baserad på Clang/LLVM 3.9), eftersom antalet tillgängliga plugins/checkers varierar mycket från version till version och växer ständigt.

introduktion

Obs: i det här blogginlägget användes clang-tidy-3.9

en typisk anrop av kommandoradsverktyget ser ut så här:

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

genom att utföra det så här kommer verktyget att skriva ut en massa varningar och anteckningar (om tillämpligt), på exakt samma sätt som Clang/GCC ger diagnostik också.

Clang-Tidy är en användbar statisk analysverktyg på egen hand med massor av olika tillgängliga pjäser, detta är dock inte i fokus för detta blogginlägg. Vi vill hellre utnyttja verktygets kraftfulla refactoring-funktioner för att modernisera vår källkod.

notering tillgängliga pjäser

köra verktyget utan några specifika kommandoradsparametrar kommer att köra standarduppsättningen av pjäser aktiveras av verktyget. Låt oss kolla vilka andra checkers den har att erbjuda (genom att passera –checks=’*’ för att se dem alla), och specifikt grep för de med modernisera i deras namn. Dessa pjäser förespråkar användning av moderna språkkonstruktioner:

$ 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

imponerande lista med alternativ redan, eller hur? Clang-Tidy skickar faktiskt några intressanta checkers ur lådan (från och med Clang/LLVM 3.9), med listan växer ständigt från release till release.

namnen på checkarna är ganska självförklarande (t. ex. modernize-use-auto kommer att omfamna med auto där det är tillämpligt), men om du vill utforska vad var och en av dem betyder, se listan över checkar på clang-tidy hemsida:

för att visa hur verktyget används låt oss fokusera på modernize-use-override checker, eftersom det är den mest tillämpliga och mest okontroversiella checker.

Refactoring en enda fil

vår åsidosätta exempel igen:

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

Running clang-tidy på exemplet (den här gången med modernize-use-override checker aktiverad):

% 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

okej. Så det märkte att Derived::reimplementMe(int) åsidosätter en basklassmetod men saknar override specificeraren! Nu kan vi lägga till det manuellt … eller bara låta verktyget göra det åt oss genom att passera-fix!

kör det på exemplet (med modernize-use-override checker & fix-det är aktiverat):

% 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 tillämpade fix-it och infogade override efter metoddeklarationen i rad 5. Klart!

ett par anteckningar

det finns några saker som är värda att nämna:

  • inte alla kontroller av clang-tidy bär faktiskt fix-its, men de som börjar med modernisera alla gör.
  • du kan använda fix-its från flera kontroller samtidigt (överväg-checks=’modernisera-använd-Åsidosätt,modernisera-använd-auto’ -fix)
  • Running clang-tidy åberopar den fullständiga clang-kompilatorfronten, vilket kommer att behöva lite tid att slutföra
  • Refactoring resultat från clang-tidy är helt korrekta, på grund av att det stöds av en fullfjädrad C++-parser

refactoring ett komplett projekt (cmake-baserat)

hittills har vi kört clang-tidy på en enda, fristående fil endast. Vad händer om du har en mer komplex Projektinställning, med massor av filer, alla med anpassade kompileringsflaggor? Clang-tidy fungerar alltid på en enda fil, eller snarare, översättningsenhet. Vi kan hjälpa verktyget att räkna ut rätt kompileringsflaggor för varje översättningsenhet vi sammanställer i vårt projekt. Det bekvämaste sättet att köra det är med en kompileringskommandodatabas. CMake kan automatiskt generera en, och en gång en compile_commands.json är på plats och en fungerande version av clang-tidy är i väg hela kodbasen kan analyseras med run-clang-tidy.Py script (vanligtvis levereras som en del av installationen). Om inte kan du helt enkelt ladda ner den här.

Obs: Det rekommenderas starkt att använda run-clang-tidy.py att köra clang-tidy på ett helt projekt, eftersom det kommer att köra verktyget flera gånger parallellt och ser till att samtidiga körningar inte stör varandra (t.ex. genom att undvika att ändra en delad rubrik parallellt och i sin tur generera trasig kod).

generera en compile_commands.json-fil

för att generera compile_commands.json-fil i ett cmake-baserat projekt, kör bara:

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

använd script för att köra clang-tidy

nu för att köra verktyget med standardkontroller på varje översättningsenhet i projektet, helt enkelt åberopa run-clang-tidy script inne i katalogen med compile_commands.json file:

% run-clang-tidy.py

som tidigare sett kommer detta inte att ändra någonting hittills, eftersom vi har kört clang-tidy med bara standardkontrollerna aktiverade. För att t. ex. köra modernize-use-override-kontrollen på alla översättningsenheter och faktiskt refactor all din kod behövs denna anrop:

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

det är det. clang-tidy kommer nu att åberopas på varje översättningsenhet i projektet och kommer att lägga åsido där saknas. Parametern-header-filter=’.* ’ser till clang-tidy faktiskt refactors kod i rubrikerna som konsumeras i översättningsenheten. Parametern checks=’ -*,… ’ ser till att alla standardkontroller är inaktiverade.

Observera att korrigeringarna endast tillämpas när run-clang-tidy är klar! Skriptet registrerar bara de ändringar som ska utföras och tillämpar dem alla på en gång i slutet.

Running other checkers

återigen är modernize-use-override bara ett exempel, clang-tidy har många andra checkers som är användbara. En annan super användbar är modernize-use-nullptr checker, som omvandlar 0, eller t.ex. NULL bokstav till modern C++11 nullptr version. För att refactor alla användningar av gammaldags bokstäver i ditt projekt, kör helt enkelt:

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

det är vanligtvis bra att utföra en kontroller efter den andra, begå mellanliggande resultat (tänk på ”Port mot C++11 nullptr”, ”Port mot C++11 override”, …) i ditt versionskontrollsystem.

några verkliga exempel

jag har personligen använt clang-tidy på många olika projekt redan, med positiva resultat. Kom ihåg att det här verktyget har perfekt kunskap om din kod (som det faktiskt använder Clang compiler frontend) och därmed kommer att refactor din kod utan att någonsin införa trasig kod.

exempel:

  • denna patch portar till exempel alla KDE: s Rambibliotek mot C++11 nullptr, genom att röra runt 9000 olika kodplatser
  • denna patch portar KDE Marble-kodbasen till C++11-åsidosättning, genom att röra runt 2300 olika kodplatser

slutsats

Clang-Tidy är ett kraftfullt verktyg för verktyg som gör portning din äldre kodbas mot C++11 en fråga om att köra en one-liner. Den levereras med en stor uppsättning standard checkers och listan över ytterligare växer ständigt. Modernize-checkers kan användas för att modernisera/refactor din källkod för att använda nya C++ språkfunktioner.

i nästa del av denna serie kommer vi att diskutera hur man använder clang-tidy projektomfattande med andra byggsystem.

behöver du hjälp?

KDAB sysselsätter flera ingenjörer som arbetar med Clang Tooling dagligen. Vi hjälper dig gärna om du har problem med att använda Clang-verktyg i ditt projekt eller vill få ut det mesta: genom att låta oss implementera projektspecifika kodkontroller eller köra automatiska refactoringverktyg på din kodbas. Vi har hjälpt kunder att modernisera sina mycket stora kodbaser framgångsrikt med hjälp av verktyg-något som inte skulle ha varit möjligt för dem kostnadseffektivt att göra det manuellt.

kontakta oss

Facebook TwitterLinkedIn e-post

kategorier: C++ / funktionell säkerhet / Hur / Kdab bloggar / KDAB på Qt/verktyg

taggar: automatiserad refactoring / C++ / C++11 / C++14 / Clang / LLVM

Kevin Funk Senior Software Engineer

Lämna ett svar

Din e-postadress kommer inte publiceras.