Dla kontekstu jestem programistą Clang pracującym w Google. W Google wdrożyliśmy diagnostykę Clanga (zasadniczo) dla wszystkich naszych programistów C ++ i traktujemy ostrzeżenia Clanga również jako błędy. Jako zarówno programista Clang, jak i jeden z większych użytkowników diagnostyki Clanga, postaram się rzucić nieco światła na te flagi i sposób ich użycia. Zauważ, że wszystko, co opisuję, ogólnie dotyczy Clanga, a nie specyficzne dla C, C ++ lub Objective-C.
TL; DR wersja: Proszę używać -Wall
i -Werror
co najmniej na każdym nowym kodzie rozwijają. My (programiści kompilatora) dodajemy tutaj ostrzeżenia z ważnych powodów: znajdują błędy. Jeśli znajdziesz ostrzeżenie, które wyłapuje błędy, włącz je również. Wypróbuj -Wextra
tutaj grupę dobrych kandydatów. Jeśli jeden z nich jest zbyt głośny, abyś mógł z niego korzystać, zgłoś błąd . Jeśli piszesz kod zawierający „oczywisty” błąd, ale kompilator go nie ostrzega, zgłoś błąd.
Teraz do długiej wersji. Najpierw trochę informacji na temat grupowania flag ostrzegawczych. Istnieje wiele „grup” ostrzeżeń w Clang (i w ograniczonym zakresie w GCC). Niektóre z nich są istotne w tej dyskusji:
- Domyślnie: te ostrzeżenia są zawsze włączone, chyba że je wyraźnie wyłączysz.
-Wall
: Są to ostrzeżenia, że programiści mają wysokie zaufanie zarówno do ich wartości, jak i niskiego wskaźnika fałszywie dodatnich.
-Wextra
: Są to ostrzeżenia, które są uważane za wartościowe i solidne (tzn. Nie są błędne), ale mogą mieć wysokie wskaźniki fałszywie dodatnich lub wspólne obiekcje filozoficzne.
-Weverything
: To szalona grupa, która dosłownie włącza każde
ostrzeżenie w Clang. Nie używaj tego w swoim kodzie. Jest przeznaczony wyłącznie dla programistów Clanga lub do zbadania istniejących ostrzeżeń .
Istnieją dwa podstawowe kryteria wspomniane powyżej, które kierują ostrzeżeniami w Clang i wyjaśnijmy, co one naprawdę oznaczają. Pierwszą jest potencjalna
wartość konkretnego wystąpienia ostrzeżenia. Jest to oczekiwana korzyść dla użytkownika (programisty), gdy ostrzeżenie zostanie uruchomione i poprawnie
zidentyfikuje problem z kodem.
Drugim kryterium jest idea fałszywie pozytywnych raportów. Są to sytuacje, w których ostrzeżenie jest uruchamiane w kodzie, ale potencjalny cytowany problem w rzeczywistości nie występuje z powodu kontekstu lub innych ograniczeń programu. Ostrzegany kod faktycznie zachowuje się poprawnie. Są one szczególnie złe, gdy ostrzeżenie nigdy nie było przeznaczone do uruchomienia dla tego wzorca kodu. Zamiast tego jest to brak implementacji ostrzeżenia, który powoduje, że tam strzela.
W przypadku ostrzeżeń Clanga wartość musi być zgodna z poprawnością , a nie ze stylem, smakiem czy konwencjami kodowania. Ogranicza to zestaw dostępnych ostrzeżeń, wykluczając często {}
pojawiające się ostrzeżenia, takie jak ostrzeżenie, ilekroć nie są używane w treści if
instrukcji. Clang jest również bardzo nietolerancyjny w stosunku do fałszywych alarmów . W przeciwieństwie do większości innych kompilatorów użyje niewiarygodnej różnorodności źródeł informacji do przycinania fałszywych trafień, w tym dokładnej pisowni konstruktu, obecności lub braku dodatkowych „()”, rzutowań, a nawet makr preprocesora!
Teraz weźmy kilka przykładowych ostrzeżeń z rzeczywistego świata od Clanga i zobaczmy, jak są one podzielone na kategorie. Po pierwsze, domyślne ostrzeżenie:
% nl x.cc
1 class C { const int x; };
% clang -fsyntax-only x.cc
x.cc:1:7: warning: class 'C' does not declare any constructor to initialize its non-modifiable members
class C { const int x; };
^
x.cc:1:21: note: const member 'x' will never be initialized
class C { const int x; };
^
1 warning generated.
Tutaj nie była wymagana flaga, aby otrzymać to ostrzeżenie. Uzasadnieniem jest to, że ten kod nigdy nie jest naprawdę poprawny, co daje ostrzeżenie o wysokiej wartości , a ostrzeżenie jest uruchamiane tylko w kodzie, który Clang może udowodnić, że wpada do tego segmentu, dając mu zerową częstość fałszywie dodatnich .
% nl x2.cc
1 int f(int x_) {
2 int x = x;
3 return x;
4 }
% clang -fsyntax-only -Wall x2.cc
x2.cc:2:11: warning: variable 'x' is uninitialized when used within its own initialization [-Wuninitialized]
int x = x;
~ ^
1 warning generated.
Clang wymaga -Wall
flagi dla tego ostrzeżenia. Powodem jest to, że istnieje nietrywialna ilość kodu, który wykorzystał (na dobre lub złe) wzorzec kodu, o którym ostrzegamy, aby celowo wygenerować niezainicjowaną wartość. Filozoficznie nie widzę w tym sensu, ale wielu innych się nie zgadza, a rzeczywistość tej różnicy zdań jest tym, co napędza ostrzeżenie pod
-Wall
flagą. Nadal ma bardzo wysoką wartość i bardzo niski
odsetek wyników fałszywie dodatnich , ale w niektórych bazach kodowych nie jest on uruchamiany.
% nl x3.cc
1 void g(int x);
2 void f(int arr[], unsigned int size) {
3 for (int i = 0; i < size; ++i)
4 g(arr[i]);
5 }
% clang -fsyntax-only -Wextra x3.cc
x3.cc:3:21: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
for (int i = 0; i < size; ++i)
~ ^ ~~~~
1 warning generated.
To ostrzeżenie wymaga -Wextra
flagi. Powodem jest to, że istnieją bardzo
duże bazy kodu, w których źle dobrany znak w porównaniu jest niezwykle powszechny. Chociaż to ostrzeżenie zawiera pewne błędy, prawdopodobieństwo, że kod jest błędem, gdy użytkownik pisze, jest średnio dość niskie. Rezultatem jest wyjątkowo wysoki odsetek wyników fałszywie dodatnich . Jednak gdy w programie występuje błąd z powodu dziwnych zasad promocji, często jest bardzo subtelny, dlatego ostrzeżenie,
gdy oznacza błąd, ma stosunkowo wysoką wartość . W rezultacie Clang zapewnia go i wystawia pod flagą.
Zazwyczaj ostrzeżenia nie żyją długo poza -Wextra
flagą. Clang bardzo stara się nie wdrażać ostrzeżeń, które nie są regularnie używane i testowane. Dodatkowe ostrzeżenia włączane przez -Weverything
są zwykle ostrzeżeniami w trakcie aktywnego opracowywania lub z aktywnymi błędami. Albo zostaną ustalone i umieszczone pod odpowiednimi flagami, albo powinny zostać usunięte.
Teraz, gdy rozumiemy, jak te rzeczy działają z Clangiem, spróbujmy wrócić do pierwotnego pytania: jakie ostrzeżenia powinieneś włączyć w swoim rozwoju? Odpowiedź brzmi niestety, że to zależy. Rozważ następujące pytania, aby ustalić, które ostrzeżenia najlepiej pasują do Twojej sytuacji.
- Czy masz kontrolę nad całym swoim kodem, czy też niektóre z nich są zewnętrzne?
- Jakie są Twoje cele? Łapiesz błędy lub piszesz lepszy kod?
- Jaka jest twoja fałszywie dodatnia tolerancja? Czy chcesz regularnie pisać dodatkowy kod, aby wyciszyć ostrzeżenia?
Przede wszystkim, jeśli nie kontrolujesz kodu, nie próbuj włączać dodatkowych ostrzeżeń. Przygotuj się na wyłączenie niektórych. Na świecie jest wiele złych kodów i możesz nie być w stanie naprawić wszystkich. To dobrze. Pracuj, aby znaleźć sposób, aby skoncentrować swoje wysiłki na kodzie, który kontrolujesz.
Następnie dowiedz się, czego chcesz z ostrzeżeń. To jest różne dla różnych ludzi. Clang spróbuje ostrzec bez żadnych rażących błędów lub wzorców kodu, dla których mamy długą historyczną precedens wskazujący, że wskaźnik błędów jest wyjątkowo wysoki. Umożliwiając -Wall
otrzymanie o wiele bardziej agresywnego zestawu ostrzeżeń mających na celu wychwycenie najczęstszych błędów, które deweloperzy Clang zaobserwowali w kodzie C ++. Ale w obu przypadkach
odsetek wyników fałszywie dodatnich powinien pozostać dość niski.
Wreszcie, jeśli jesteś całkowicie gotów uciszyć * fałszywie dodatnie * za każdym razem, idź -Wextra
. Zgłaszaj błędy, jeśli zauważysz ostrzeżenia, które wyłapują wiele prawdziwych błędów, ale które mają głupie lub bezcelowe fałszywe alarmy. Nieustannie pracujemy, aby znaleźć sposoby, aby przynieść więcej i więcej logiki błędów obecnych w rozpoznawczej -Wextra
w -Wall
którym możemy uniknąć fałszywych alarmów.
Wielu przekona się, że żadna z tych opcji nie jest dla nich odpowiednia. W Google -Wall
wyłączyliśmy niektóre ostrzeżenia z powodu dużej ilości istniejącego kodu, który naruszył ostrzeżenie. Włączyliśmy też jawnie niektóre ostrzeżenia, nawet jeśli nie są one włączone -Wall
, ponieważ mają dla nas szczególnie wysoką wartość. Twój przebieg będzie się różnić, ale prawdopodobnie będzie się różnić w podobny sposób. Często lepiej jest włączyć kilka kluczowych ostrzeżeń niż wszystkie
-Wextra
.
Zachęcam wszystkich do włączenia -Wall
dowolnego nie-starszego kodu. W przypadku nowego kodu ostrzeżenia tutaj są prawie zawsze cenne i naprawdę ułatwiają tworzenie kodu. I odwrotnie, zachęcam wszystkich, aby
nie włączali flag poza nimi -Wextra
. Jeśli znajdziesz ostrzeżenie Clanga, które -Wextra
nie obejmuje, ale które okazuje się dla ciebie cenne, po prostu zgłoś błąd, a my prawdopodobnie możemy go zaliczyć -Wextra
. To, czy jawnie włączysz jakiś podzbiór ostrzeżeń, -Wextra
będzie w dużym stopniu zależeć od twojego kodu, twojego stylu kodowania i od tego, czy utrzymanie tej listy jest łatwiejsze niż naprawianie wszystkiego, co nie zostało odkryte -Wextra
.
Z listy ostrzeżeń PO (która zawiera oba -Wall
i -Wextra
) tylko następujące ostrzeżenia nie są objęte tymi dwiema grupami (lub domyślnie włączone). Pierwsza grupa podkreśla, dlaczego nadmierne poleganie na wyraźnych flagach ostrzegawczych może być złe: żadna z nich nie jest nawet zaimplementowana w Clang! Są akceptowane w wierszu poleceń tylko ze względu na zgodność z GCC.
-Wbad-function-cast
-Wdeclaration-after-statement
-Wmissing-format-attribute
-Wmissing-noreturn
-Wnested-externs
-Wnewline-eof
-Wold-style-definition
-Wredundant-decls
-Wsequence-point
-Wstrict-prototypes
-Wswitch-default
Następna grupa niepotrzebnych ostrzeżeń z oryginalnej listy to te, które są zbędne z innymi na tej liście:
-Wformat-nonliteral
-- Podzbiór -Wformat=2
-Wshorten-64-to-32
-- Podzbiór -Wconversion
-Wsign-conversion
-- Podzbiór -Wconversion
Istnieje również wybór ostrzeżeń, które są bardziej kategorycznie różne. Mają one do czynienia z wariantami dialektów językowych, a nie z błędnym lub niepoprawnym kodem. Z wyjątkiem -Wwrite-strings
tych, wszystkie są ostrzeżeniami dla rozszerzeń języka dostarczanych przez Clang. To, czy Clang ostrzega przed ich użyciem, zależy od rozpowszechnienia rozszerzenia. Clang dąży do kompatybilności z GCC, a więc w wielu przypadkach ułatwia to dzięki niejawnym rozszerzeniom języka, które są szeroko stosowane. -Wwrite-strings
, jak skomentował OP, to flaga zgodności z GCC, która faktycznie zmienia semantykę programu. Głęboko żałuję tej flagi, ale musimy ją poprzeć ze względu na jej dziedzictwo.
-Wfour-char-constants
-Wpointer-arith
-Wwrite-strings
Pozostałe opcje, które faktycznie umożliwiają potencjalnie interesujące ostrzeżenia, to:
-Wcast-align
-Wconversion
-Wfloat-equal
-Wformat=2
-Wimplicit-atomic-properties
-Wmissing-declarations
-Wmissing-prototypes
-Woverlength-strings
-Wshadow
-Wstrict-selector-match
-Wundeclared-selector
-Wunreachable-code
Powód, dla którego ich nie ma -Wall
lub -Wextra
nie zawsze jest jasny. Dla wielu z nich, są one faktycznie na podstawie ostrzeżeń (GCC -Wconversion
,
-Wshadow
itp) i jako takie Clang próbuje naśladować zachowania GCC. Powoli dzielimy niektóre z nich na bardziej szczegółowe i przydatne ostrzeżenia. Ci mają większe prawdopodobieństwo, że znajdą się w jednej z grup ostrzegawczych najwyższego poziomu. To powiedziawszy, aby wybrać jedno ostrzeżenie, -Wconversion
jest tak szerokie, że prawdopodobnie pozostanie własną kategorią „najwyższego poziomu” w dającej się przewidzieć przyszłości. Niektóre inne ostrzeżenia, które GCC ma, ale które mają niską wartość i wysokie wskaźniki fałszywie dodatnich, mogą zostać przeniesione na podobną ziemię niczyją.
Inne powody, dla których nie ma ich w jednym z większych koszyków, to proste błędy, bardzo znaczące fałszywie pozytywne problemy i ostrzeżenia w trakcie opracowywania. Zajmę się zgłaszaniem błędów dla tych, które mogę zidentyfikować. Wszyscy powinni ostatecznie migrować do odpowiedniej flagi dużego wiadra lub zostać usunięci z Clang.
Mam nadzieję, że to wyjaśnia sytuację ostrzegawczą z Clangiem i zapewnia pewien wgląd dla tych, którzy próbują wybrać zestaw ostrzeżeń dla ich zastosowania lub użytkowania ich firmy.