Clang-Tidy, Część 1: modernizacja kodu źródłowego przy użyciu C++11 / C++14automatyczna refaktoryzacja kodu źródłowego przy użyciu potężnych narzędzi open-source

16.03.2017 Kevin Funk

 Facebook  Twitter  LinkedIn  e-mail

ta seria blogów przedstawi narzędzie clang-tidy z projektu clang / LLVM i pokaże, jak go użyć do automatycznego refaktorowania kodu źródłowego C++ i integracji z systemem kompilacji, a także Jak korzystać z narzędzia na innych platformach niż Unices.

Motivation: the joy of legacy code bases

C++11 dodał znaczną ilość nowych funkcji języka C++, które do tej pory nie są w pełni wykorzystywane. Najbardziej wizualne to z pewnością auto, override, wyrażenia Lambda, oparte na zakresie for, jednolita składnia inicjalizacji, nazwij to. Podczas gdy C++11 ma już kilka lat, nadal istnieje wiele baz kodu, które nie używają żadnych nowych funkcji językowych, czy to przez politykę zarządzania, czy przez czyste lenistwo, aby uniknąć przenoszenia wysiłku ze strony programisty. Clang-Tidy z projektu LLVM compiler infrastructure jest tutaj, aby przynajmniej przezwyciężyć ten drugi, aby umożliwić automatyczną refaktoryzację kodu źródłowego, aby używał nowych funkcji językowych.

niech ktoś użyje?

teraz, co się stanie, jeśli utrzymasz już dość dużą bazę kodu, która nadal jest kompilowana w trybie C++03, ale chcesz korzystać z C++11 w przyszłości, ze wszystkimi przydatnymi funkcjami, które ma? Prawdopodobnie będziesz miał wiele, wiele kodu podobnego do tego:

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

do tej pory oczywiście zawsze ponownie zaimplementowałeś poprawną metodę base-class, ponieważ dokładnie przetestowałeś swój kod za pomocą testów jednostkowych! Oczywiście, że tak. Tak więc kod jest w porządku, ale teraz chcesz przejść dalej. Chcesz dodać specyfikator override do każdej ponownie zaimplementowanej metody w bazie kodu, ale oczywiście bez zatrudniania stażysty, który przechodzi przez kod linia po linii i dodaje je ręcznie.

aby podkreślić, że dodawanie override rzeczywiście służy celowi, niedawno naprawiliśmy błąd w Qt 3D, w którym nie przeciążaliśmy poprawnej metody base-class. Przy wcześniej dodanym specyfikatorze zauważylibyśmy natychmiast, po rekompilacji.

weźmiemy przykład missing-override dalej, aby wyjaśnić podstawowe użycie clang-tidy.

Clang-Tidy na ratunek!

Clang-Tidy to oparte na C++ Narzędzie linter, które dostarcza plik wykonywalny powłoki o nazwie clang-tidy jako główny punkt wejścia. Jest to rozszerzalny framework do diagnozowania typowych błędów programowania lub problemów ze stylem-ogólnie wszystkiego, co można wykryć podczas statycznej analizy kodu. Prawdziwą zaletą tego narzędzia jest to, że dodatkowo pozwala na automatyczną refaktoryzację kodu źródłowego poprzez zastosowanie poprawek, które może zapewnić każdy pojedynczy problem. Jest mocno oparty na wtyczkach i zawiera przydatny zestaw wtyczek po wyjęciu z pudełka, który omówimy w następnym akapicie.

logo LLVM -- home Of clang-tidy

Clang-Tidy to narzędzie opracowane i utrzymywane przez społeczność Clang/LLVM.

Konfiguracja

podczas uruchamiania Linuksa, clang-tidy jest zwykle łatwy do uzyskania za pośrednictwem menedżera pakietów twojej dystrybucji. Na przykład w systemie Ubuntu Linux instalacja jest tak prosta, jak uruchomienie następujących poleceń:

% sudo apt-get install clang-tidy

w jednym z nadchodzących wpisów na blogu omówimy instalację narzędzia na innych platformach niż Linux.

Uwaga: Zalecamy, aby zawsze instalować najnowszą wersję (w momencie pisania zaleca się wersję opartą na Clang / LLVM 3.9), ponieważ liczba dostępnych wtyczek/kontrolerów różni się znacznie w zależności od wersji i stale rośnie.

wprowadzenie

Uwaga: w tym poście na blogu użyto clang-tidy-3.9

typowe wywołanie narzędzia wiersza poleceń wygląda następująco:

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

wykonując to w ten sposób, narzędzie wydrukuje kilka ostrzeżeń i notatek (jeśli dotyczy), w dokładnie taki sam sposób, w jaki clang/GCC zapewnia również diagnostykę.

Clang-Tidy to samo w sobie użyteczne narzędzie do analizy statycznej z wieloma różnymi warcabami, jednak nie jest to główny temat tego posta na blogu. Wolimy wykorzystać potężne możliwości refaktoryzacji narzędzia, aby unowocześnić nasz kod źródłowy.

lista dostępnych kontrolerów

uruchomienie narzędzia bez żadnych określonych parametrów wiersza poleceń spowoduje uruchomienie domyślnego zestawu kontrolerów włączonych przez narzędzie. Sprawdźmy, jakie inne Warcaby ma do zaoferowania (przechodząc –checks=’*’, aby zobaczyć je wszystkie), a konkretnie grep dla tych z modernize w ich nazwach. Warcaby te opowiadają się za wykorzystaniem nowoczesnych konstrukcji językowych:

$ 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

imponująca lista opcji, prawda? Clang-Tidy w rzeczywistości dostarcza kilka ciekawych warcabów z pudełka (od Clang / LLVM 3.9), z listą stale rosnącą od wydania do wydania.

nazwy warcabów są w zasadzie oczywiste (np. modernize-use-auto obejmie użycie auto tam, gdzie ma to zastosowanie), ale jeśli chcesz dowiedzieć się, co każdy z nich oznacza, zapoznaj się z listą warcabów na stronie głównej clang-tidy:

aby pokazać, w jaki sposób narzędzie jest używane, skupmy się na sprawdzaniu modernize-use-override, ponieważ jest to najbardziej odpowiedni i najbardziej niekontrowersyjny kontroler.

Refaktoryzacja pojedynczego pliku

nasz przykład nadpisania ponownie:

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

uruchomienie clang-tidy na przykładzie (tym razem z włączonym kontrolerem modernize-use-override):

% 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

w porządku. Więc zauważył, że Derived::reimplementMe(int) nadpisuje metodę klasy bazowej, ale nie ma specyfikatora override! Teraz możemy dodać to ręcznie … lub po prostu pozwolić narzędziu zrobić to za nas, przekazując-fix!

uruchamianie go na przykładzie (z modernize-use-override checker & fix-its enabled):

% 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 zastosował fix-it i wstawił override po deklaracji metody w wierszu 5. Zrobione!

kilka uwag

jest kilka rzeczy, o których warto wspomnieć:

  • nie wszystkie Warcaby clang-tidy faktycznie noszą fix-its, ale te, które zaczynają się od modernizacji, wszystkie robią.
  • możesz użyć fix-its z wielu kontrolerów w tym samym czasie (rozważ-checks=’modernize-use-override,modernize-use-auto’ -fix)
  • uruchomienie clang-tidy wywołuje kompletny interfejs kompilatora clang, więc będzie potrzebował trochę czasu, aby ukończyć
  • Refaktoryzacja wyniki z clang-tidy są doskonale dokładne, ze względu na fakt, że jest wspierany przez w pełni fledged c++ parser

refaktoryzacja kompletnego projektu (opartego na CMake)

do tej pory uruchomiliśmy clang-tidy tylko na jednym, samodzielnym pliku. Co się stanie, jeśli masz bardziej złożoną konfigurację projektu, z dużą ilością plików, wszystkie z niestandardowymi flagami kompilacji? Clang-tidy zawsze działa na jednym pliku, a raczej na jednostce tłumaczenia. Możemy pomóc narzędziu, aby dowiedzieć się poprawne flagi kompilacji dla każdej jednostki tłumaczeniowej kompilujemy w naszym projekcie. Najwygodniejszym sposobem jego uruchomienia jest skompilowanie bazy danych poleceń. CMake może automatycznie wygenerować jeden, a raz compile_commands.json jest na miejscu, a działająca wersja clang-tidy jest na ścieżce cała baza kodu może być analizowana za pomocą run-clang-tidy.skrypt py (zwykle dostarczany jako część instalacji). Jeśli nie, możesz go po prostu pobrać tutaj.

Uwaga: zaleca się stosowanie run-clang-tidy.py aby uruchomić clang-tidy na całym projekcie, ponieważ będzie on uruchamiał narzędzie wiele razy równolegle i upewnia się, że jednoczesne wykonywanie nie koliduje ze sobą (np. unikając modyfikowania współdzielonego nagłówka równolegle i z kolei generując zepsuty kod).

generowanie kompilacji.plik json

do generowania poleceń kompilacji.plik json w projekcie opartym na CMake, wystarczy uruchomić:

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

Użyj skryptu, aby uruchomić clang-tidy

teraz, aby uruchomić narzędzie z domyślnym sprawdzaniem każdej jednostki tłumaczenia w projekcie, po prostu wywołaj skrypt run-clang-tidy wewnątrz katalogu z compile_commands.plik json:

% run-clang-tidy.py

jak widać wcześniej, do tej pory nic nie zmieni, ponieważ uruchomiliśmy clang-tidy z włączonymi tylko domyślnymi sprawdzeniami. Aby np. uruchomić modernize-use-override check na wszystkich jednostkach tłumaczeń i faktycznie refaktorować cały kod, to wywołanie jest potrzebne:

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

to wszystko. clang-tidy będzie teraz wywoływany na każdej jednostce tłumaczeniowej w Twoim projekcie i doda przesłonięcia tam, gdzie ich brakuje. Parametr-header-filter=’.* 'upewnia się, że clang-tidy faktycznie refaktoruje kod w nagłówkach używanych w jednostce tłumaczeniowej. Parametr checks=’ -*,… ’ zapewnia, że wszystkie domyślne kontrole są wyłączone.

zauważ, że poprawki są stosowane tylko po zakończeniu run-clang-tidy! Skrypt rejestruje tylko zmiany do wykonania i stosuje je wszystkie naraz na końcu.

uruchamianie innych warcabów

ponownie modernize-use-override jest tylko przykładem, clang-tidy ma wiele innych warcabów, które są przydatne. Innym bardzo użytecznym narzędziem jest modernize-use-nullptr checker, który przekształca literały 0 lub np. NULL w nowoczesną wersję C++11 nullptr. Aby zreformować wszystkie zastosowania literałów starego stylu w Twoim projekcie, po prostu uruchom:

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

zwykle dobrym pomysłem jest wykonywanie jednego kontrolera po drugim, zatwierdzając pośrednie wyniki (pomyśl o „Port towards C++11 nullptr”, „Port towards C++11 override”, …) w systemie kontroli wersji.

kilka przykładów z prawdziwego świata

osobiście używałem clang-tidy już w wielu różnych projektach, z pozytywnymi wynikami. Pamiętaj, że to narzędzie ma doskonałą wiedzę o Twoim kodzie (tak jak w rzeczywistości używa nakładki kompilatora clang), a tym samym będzie refaktorować Twój kod bez wprowadzania zepsutego kodu.

przykłady:

  • ta łata na przykład przekierowuje wszystkie biblioteki frameworków KDE do C++11 nullptr, dotykając około 9000 różnych lokalizacji kodu
  • ta łata przekierowuje bazę kodu KDE Marble do nadpisania kodu C++11, dotykając około 2300 różnych lokalizacji kodu

wniosek

Clang-Tidy jest potężnym narzędziem, które pozwala sprawia, że przenoszenie twojego starszego kodu w kierunku C++11 jest kwestią uruchomienia One-Liner. Jest wyposażony w świetny zestaw domyślnych warcabów, a lista dodatkowych stale rośnie. Modernize-checkers mogą być używane do modernizacji / refaktorowania kodu źródłowego, aby korzystać z nowych funkcji języka C++.

w następnej części tej serii omówimy, jak używać clang-tidy project-wide z innymi systemami budowania.

potrzebujesz pomocy?

KDAB zatrudnia kilku inżynierów, którzy na co dzień pracują z narzędziami Clang. Chętnie pomożemy Ci w przypadku problemów z użyciem narzędzi Clang w swoim projekcie lub chcesz uzyskać jak najwięcej z niego: pozwalając nam wdrożyć kontrole kodu specyficzne dla projektu lub uruchomić automatyczne narzędzia do refaktoryzacji na podstawie kodu. Pomogliśmy klientom zmodernizować ich bardzo duże bazy kodu z powodzeniem przy użyciu narzędzi – coś, co nie byłoby dla nich wykonalne pod względem kosztów, robiąc to ręcznie.

skontaktuj się z nami

Facebook  Twitter  LinkedIn  e-mail

Kategorie: C++ / Bezpieczeństwo funkcjonalne / jak / Kdab Blogs / Kdab on Qt / Tooling

Tagi: automatyczna refaktoryzacja / C++ / C++11 / C++14 / Clang / LLVM

Kevin Funk starszy inżynier oprogramowania

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.