Clang flag di avviso per lo sviluppo di Objective-C


86

Come programmatore C & Objective-C, sono un po 'paranoico con i flag di avvertimento del compilatore.
Di solito cerco di trovare un elenco completo di flag di avviso per il compilatore che uso e di accenderne la maggior parte, a meno che non abbia davvero un buon motivo per non accenderlo.

Personalmente penso che questo possa effettivamente migliorare le capacità di codifica, nonché la potenziale portabilità del codice, prevenire alcuni problemi, poiché ti costringe a essere consapevole di ogni piccolo dettaglio, potenziali problemi di implementazione e architettura e così via ...

È anche secondo me un valido strumento di apprendimento quotidiano, anche se sei un programmatore esperto.

Per la parte soggettiva di questa domanda, sono interessato a sentire altri sviluppatori (principalmente C, Objective-C e C ++) su questo argomento.
Ti interessa davvero cose come avvertimenti pedanti, ecc.? E se sì o no, perché?

Ora su Objective-C, di recente sono passato completamente alla toolchain LLVM (con Clang), anziché a GCC.

Sul mio codice di produzione, di solito imposto questi flag di avviso (esplicitamente, anche se alcuni di essi potrebbero essere coperti da -Wall):

  • -Parete
  • -Wbad-function-fusione
  • -Wcast-align
  • -Wconversion
  • -Wdeclaration-dopo-statement
  • -Wdeprecated-implementazioni
  • -Wextra
  • -Wfloat-pari
  • -Wformat = 2
  • -Wformat-non letterale
  • -Wfour-char-costanti
  • -Wimplicit-atomico-properties
  • -Wmissing graffe
  • -Wmissing-dichiarazioni
  • -Wmissing-field-inizializzatori
  • -Wmissing formato attributo
  • -Wmissing-noreturn
  • -Wmissing-prototipi
  • -Wnested-externs
  • -Wnewline-eof
  • -Wold-style-definizione
  • -Woverlength-stringhe
  • -Wparentheses
  • -Wpointer-arith
  • -Wredundant-decls
  • -Wreturn tipo
  • -Wsequence-point
  • -Wshadow
  • -Wshorten-64-a-32
  • -Wsign-confrontare
  • -Wsign-conversione
  • -Wstrict-prototipi
  • -Wstrict selettore-partita
  • -Wswitch
  • -Wswitch-default
  • -Wswitch-enum
  • -Wundeclared selettore
  • -Wuninitialized
  • -Wunknown-pragma
  • -Wunreachable-code
  • -Wunused funzione
  • -Wunused-label
  • -Wunused parametri
  • -Wunused valore
  • -Wunused-variabile
  • -Wwrite-stringhe

Sono interessato a sapere cosa dicono gli altri sviluppatori a riguardo.

Ad esempio, pensi che mi sia sfuggita una bandiera particolare per Clang (Objective-C), e perché?
O pensi che una particolare bandiera non sia utile (o non lo desideri affatto), e perché?

MODIFICARE

Per chiarire la domanda, si noti che -Wallfornisce solo alcuni avvisi di base.
In realtà sono molte più bandiere di avvertimento, non coperte da -Wall, quindi la domanda e l'elenco che fornisco.


9
Sono tentato di dire che probabilmente avrebbe dovuto rimanere su Stack Overflow, dal momento che è una specie di domanda sull'uso degli strumenti, ma vedremo come oscilla la community.
Adam Lear

Grazie per il commento:) In un certo senso, sono d'accordo con te, ed è per questo che inizialmente l'ho pubblicato su StackOverflow (anche perché non sono un utente normale qui, peccato per me). Ho avuto molti punti di vista, voti positivi, ecc ... Ma non una sola risposta ... E come la gente ha suggerito di spostarlo ... Bene, vediamo
:)

Spero di poterti dare una buona risposta. In bocca al lupo. :)
Adam Lear

Per C e gcc, lo -Wwrite-stringsrende un compilatore di un linguaggio molto simile ma non esattamente C. Per questo motivo da solo non uso quell'opzione. Oltre a quelli specificati, io uso -pedantic -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wlogical-op -Wstrict-aliasing=2e -Wno-missing-braces -Wno-missing-field-initializersper l'inizializzatore perfettamente ragionevole struct whatever obj = {0};. Inoltre trovo che -Wconversionfornisca più "spam" che avvisi utili :)
pmg

Risposte:


158

Per il contesto, sono uno sviluppatore di Clang che lavora in Google. In Google, abbiamo implementato la diagnostica di Clang su (essenzialmente) tutti i nostri sviluppatori C ++ e trattiamo anche gli avvisi di Clang come errori. Come sia uno sviluppatore di Clang che uno dei maggiori utenti della diagnostica di Clang, proverò a far luce su queste bandiere e su come possono essere utilizzate. Nota che tutto ciò che sto descrivendo è genericamente applicabile a Clang e non specifico a C, C ++ o Objective-C.

TL; DR Versione: Si prega di utilizzare -Walle -Werrorcome minimo su ogni nuovo codice che si sta sviluppando. Noi (gli sviluppatori del compilatore) aggiungiamo avvisi qui per buoni motivi: trovano bug. Se trovi un avviso che rileva i bug per te, attivalo pure. Prova qui -Wextraun sacco di buoni candidati. Se uno di questi è troppo rumoroso per essere utilizzato in modo redditizio, invia un bug . Se scrivi un codice che contiene un bug "ovvio" ma il compilatore non ti ha avvisato, invia un bug.

Ora per la versione lunga. Prima alcuni retroscena sui raggruppamenti di bandiere di avvertimento. Ci sono molti "raggruppamenti" di avvertimenti in Clang (e in misura limitata nel GCC). Alcuni che sono rilevanti per questa discussione:

  • Attivo per impostazione predefinita: questi avvisi sono sempre attivi, a meno che non vengano disabilitati esplicitamente.
  • -Wall: Questi sono avvertimenti che gli sviluppatori hanno un'alta fiducia sia nel loro valore che in un basso tasso di falsi positivi.
  • -Wextra: Si tratta di avvertimenti ritenuti preziosi e validi (ovvero non corretti), ma possono avere alti tassi di falsi positivi o obiezioni filosofiche comuni.
  • -Weverything: Questo è un gruppo folle che abilita letteralmente ogni avvertimento in Clang. Non usare questo sul tuo codice. È destinato esclusivamente agli sviluppatori Clang o all'esplorazione di quali avvisi esistono .

Ci sono due criteri principali sopra menzionati che guidano dove vanno gli avvisi a Clang, e chiariamo cosa significano veramente. Il primo è il valore potenziale di una particolare occorrenza dell'avviso. Questo è il vantaggio atteso per l'utente (sviluppatore) quando viene generato l'avviso e identifica correttamente un problema con il codice.

Il secondo criterio è l'idea di rapporti falsi positivi . Queste sono situazioni in cui l'avviso si attiva sul codice, ma il potenziale problema citato non si verifica in realtà a causa del contesto o di qualche altro vincolo del programma. Il codice avvisato in realtà si sta comportando correttamente. Questi sono particolarmente dannosi quando l'avviso non è mai stato progettato per attivare quel modello di codice. Invece, è una carenza nell'implementazione dell'avvertenza che lo fa sparare lì.

Per gli avvisi di Clang, il valore deve essere in termini di correttezza , non in termini di stile, gusto o convenzioni di codifica. Ciò limita la serie di avvisi disponibili, escludendo avvisi spesso richiesti come avvisi ogni volta {}che non vengono utilizzati attorno al corpo di ifun'istruzione. Clang è anche molto intollerante ai falsi positivi . A differenza della maggior parte degli altri compilatori, utilizzerà un'incredibile varietà di fonti di informazioni per eliminare i falsi positivi, incluso l'ortografia esatta del costrutto, presenza o assenza di "()" extra, cast o persino macro di preprocessore!

Ora prendiamo alcuni avvertimenti di esempio del mondo reale di Clang e vediamo come sono classificati. Innanzitutto, un avviso predefinito:

% 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.

Qui non è stata richiesta la bandiera per ricevere questo avviso. La logica è che questo codice non è mai veramente corretto, dando l'avvertimento alto valore , e l'avvertimento si attiva solo sul codice che Clang può provare cade in questo bucket, dandogli un tasso zero di falsi positivi .

% 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 richiede la -Wallbandiera per questo avviso. Il motivo è che esiste una quantità non banale di codice che ha usato (nel bene o nel male) il modello di codice che stiamo avvertendo per produrre intenzionalmente un valore non inizializzato. Filosoficamente, non vedo alcun punto in questo, ma molti altri non sono d'accordo e la realtà di questa differenza di opinione è ciò che guida l'avvertimento sotto la -Wallbandiera. Ha ancora un valore molto alto e un tasso di falsi positivi molto basso , ma su alcune basi di codice è un non-avviatore.

% 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.

Questo avviso richiede la -Wextrabandiera. Il motivo è che ci sono basi di codice molto grandi in cui il segno di corrispondenza errata nei confronti è estremamente comune. Mentre questo avviso rileva alcuni bug, la probabilità che il codice sia un bug quando l'utente lo scrive è in media abbastanza bassa. Il risultato è un tasso di falsi positivi estremamente elevato . Tuttavia, quando c'è un bug in un programma a causa delle strane regole di promozione, spesso è estremamente sottile dare questo avviso quando segnala un bug ha un valore relativamente alto . Di conseguenza, Clang lo fornisce e lo espone sotto una bandiera.

In genere, gli avvisi non vivono a lungo al di fuori della -Wextrabandiera. Clang si impegna a fondo per non implementare avvisi che non vedono un uso e test regolari. Gli avvisi aggiuntivi attivati -Weverythingsono in genere avvisi in fase di sviluppo attivo o con bug attivi. O saranno riparati e posizionati sotto le bandiere appropriate, o dovrebbero essere rimossi.

Ora che abbiamo capito come funzionano queste cose con Clang, proviamo a tornare alla domanda originale: quali avvertenze dovresti attivare per il tuo sviluppo? La risposta è, sfortunatamente, che dipende. Considera le seguenti domande per determinare quali avvisi funzionano meglio per la tua situazione.

  • Hai il controllo su tutto il tuo codice o è in parte esterno?
  • Quali sono i tuoi obiettivi? Catturare bug o scrivere codice migliore?
  • Qual è la tua tolleranza ai falsi positivi? Desideri scrivere codice aggiuntivo per mettere a tacere gli avvisi su base regolare?

Innanzitutto, se non controlli il codice, non provare a attivare ulteriori avvisi. Preparati a spegnerne un po '. Esistono molti codici errati nel mondo e potresti non essere in grado di risolverli tutti. Va bene. Lavora per trovare un modo per concentrare i tuoi sforzi sul codice che controlli.

Quindi, capire cosa vuoi dai tuoi avvertimenti. Questo è diverso per persone diverse. Clang proverà ad avvisare senza alcuna opzione su bachi egregi, o modelli di codice per i quali abbiamo un precedente storico lungo che indica che la percentuale di bug è estremamente alta. Consentendo -Walldi ottenere una serie molto più aggressiva di avvisi mirati a rilevare gli errori più comuni che gli sviluppatori di Clang hanno osservato nel codice C ++. Ma con entrambi i tassi di falsi positivi dovrebbero rimanere piuttosto bassi.

Infine, se sei perfettamente disposto a mettere a tacere * falsi positivi * s ad ogni turno, procedi -Wextra. File bug se noti avvisi che stanno rilevando molti bug reali, ma che hanno falsi positivi sciocchi o inutili. Lavoriamo costantemente per trovare modi per portare sempre più la logica di ricerca dei bug -Wextrain -Wallcui possiamo evitare i falsi positivi.

Molti scopriranno che nessuna di queste opzioni è giusta per loro. In Google, abbiamo disattivato alcuni avvisi a -Wallcausa di un sacco di codice esistente che ha violato l'avviso. Abbiamo anche attivato alcuni avvisi esplicitamente, anche se non sono abilitati da -Wall, perché hanno un valore particolarmente elevato per noi. Il tuo chilometraggio varierà, ma probabilmente varierà in modi simili. Spesso può essere molto meglio abilitare alcuni avvisi chiave piuttosto che tutti -Wextra.

Vorrei incoraggiare tutti ad attivare -Wallqualsiasi codice non legacy. Per il nuovo codice, gli avvisi qui sono quasi sempre preziosi e rendono davvero migliore l'esperienza di sviluppo del codice. Al contrario, incoraggerei tutti a non abilitare le bandiere oltre -Wextra. Se si trova un avvertimento Clang che -Wextra non include, ma che si rivela affatto prezioso per voi, è sufficiente aprire un bug e possiamo probabilmente metterla sotto -Wextra. L'abilitazione esplicita di alcuni sottoinsiemi degli avvisi -Wextradipenderà fortemente dal codice, dallo stile di codifica e dal fatto che mantenere tale elenco sia più semplice che correggere tutto ciò che viene scoperto -Wextra.

Dell'elenco di avvisi del PO (che includeva entrambi -Walle -Wextra) solo i seguenti avvisi non sono coperti da questi due gruppi (o attivati ​​per impostazione predefinita). Il primo gruppo sottolinea perché l'eccessiva dipendenza dai flag di avvertimento espliciti può essere negativo: nessuno di questi è nemmeno implementato in Clang! Sono accettati dalla riga di comando solo per compatibilità 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

Il prossimo bucket di avvisi non necessari nell'elenco originale sono quelli che sono ridondanti con gli altri in tale elenco:

  • -Wformat-nonliteral - Sottoinsieme di -Wformat=2
  • -Wshorten-64-to-32 - Sottoinsieme di -Wconversion
  • -Wsign-conversion - Sottoinsieme di -Wconversion

Ci sono anche una selezione di avvisi che sono più categoricamente diversi. Si tratta di varianti del dialetto linguistico piuttosto che di codici errati o non corretti. Con l'eccezione di -Wwrite-strings, tutti questi sono avvisi per le estensioni di lingua fornite da Clang. Se Clang avverte del loro uso dipende dalla prevalenza dell'estensione. Clang mira alla compatibilità GCC, e quindi in molti casi lo facilita con estensioni di linguaggio implicite che sono ampiamente utilizzate. -Wwrite-strings, come commentato sull'OP, è un flag di compatibilità di GCC che modifica effettivamente la semantica del programma. Mi rammarico profondamente per questa bandiera, ma dobbiamo sostenerla a causa dell'eredità che ha ora.

  • -Wfour-char-constants
  • -Wpointer-arith
  • -Wwrite-strings

Le restanti opzioni che stanno effettivamente abilitando avvisi potenzialmente interessanti sono queste:

  • -Wcast-align
  • -Wconversion
  • -Wfloat-equal
  • -Wformat=2
  • -Wimplicit-atomic-properties
  • -Wmissing-declarations
  • -Wmissing-prototypes
  • -Woverlength-strings
  • -Wshadow
  • -Wstrict-selector-match
  • -Wundeclared-selector
  • -Wunreachable-code

Il motivo per cui questi non sono presenti -Wallo -Wextranon è sempre chiaro. Per molti di questi, sono in realtà basate su avvertimenti GCC ( -Wconversion, -Wshadow, ecc) e come tale Clang cerca di imitare il comportamento di GCC. Stiamo lentamente suddividendo alcuni di questi in avvisi più dettagliati e utili. Quelli hanno quindi una maggiore probabilità di trasformarlo in uno dei gruppi di avvertimento di livello superiore. Detto questo, per cogliere un avvertimento, -Wconversionè così ampio che probabilmente rimarrà la sua categoria di "livello superiore" per il prossimo futuro. Alcuni altri avvertimenti che GCC ha ma che hanno un basso valore e alti tassi di falsi positivi possono essere relegati in una terra di nessuno simile.

Altri motivi per cui questi non si trovano in uno dei bucket più grandi includono semplici bug, problemi falsi positivi molto significativi e avvisi di sviluppo. Esaminerò i bug di archiviazione per quelli che posso identificare. Dovrebbero infine migrare in una grande benna o essere rimossi da Clang.

Spero che questo chiarisca la situazione di avvertimento con Clang e fornisca alcuni spunti per coloro che cercano di scegliere una serie di avvisi per il loro uso o l'uso della loro azienda.


8
Risposta eccezionale! Molte grazie. Dovrò rileggerlo alcune volte, ma tornerò presto con alcuni commenti. Grazie ancora!
Macmade,

1
@Chandler Carruth: ottima risposta! Ma potresti aggiornarlo, se qualcosa è cambiato? Sto usando tutti i tuoi avvisi nel tuo ultimo elenco, ma forse alcuni sono già stati spostati su Wextra?
Gartenriese,

Questo è un post eccellente. Ma trovare nuove avvertenze è, ho trovato un estremamente buon uso di una bandiera, quindi sono deluso che ti sembra così negativamente disposti verso il gruppo di "tutto", nonostante riconoscendo che è utile per questo tipo di esplorazione.
Kyle Strand,

@Chandler Carruth: sono d'accordo con la logica alla base dell'avvertimento sul codice "mai veramente corretto". È solo che non dover -Wnospegnere warn_no_constructor_for_refconst rende PITA l'uso di Boost.ConceptCheck e simili: github.com/boostorg/concept_check/blob/…
mloskot

2

Non uso Clang, ma spero che anche tu non ti dispiaccia avere la mia opinione. Vado anche con il livello di avviso massimo (suppongo che sia quello che significa -Wall) e con il trattamento degli avvisi come errori (suppongo che sia il significato di -Werror) e disabilito solo quei pochi avvisi che non hanno molto senso. In realtà, sono abbastanza infastidito dal fatto che alcuni compilatori C # non emettono alcuni avvisi molto utili e per vederli è necessario eseguire speciali strumenti di analisi del codice. Mi piacciono gli avvisi, perché mi aiutano a rilevare piccoli errori e bug nel mio codice. Mi piacciono così tanto che, quando viene emesso un avviso per un pezzo di codice che so essere corretto, rifletto comunque quel pezzo di codice, in modo che l'avviso non venga emesso, piuttosto che disabilitare l'avviso, oppure - anche peggio-- permetti che appaia e ignoralo. Penso che gli avvisi siano fantastici,


1
Grazie per la risposta. Sfortunatamente, -Wallfornisce solo avvisi di base. Molti flag di avvertimento esistenti non sono coperti da -Wall, quindi l'elenco nella mia domanda.
Macmade il

0

In genere vado con le impostazioni predefinite di Xcode per un nuovo progetto; fornisce un buon equilibrio tra utile e fastidioso. Qualsiasi elenco specifico di avvisi e altre opzioni del compilatore si baserà in gran parte su preferenze personali o requisiti di progetto, quindi non credo che ci sia molto valore nel cercare di scorrere l'elenco e discutere a favore o contro avvisi specifici. Se funziona per te, fantastico. Se ti accorgi di aver commesso un errore che il compilatore dovrebbe essere in grado di rilevare e desideri aiutarlo in futuro, cerca un'opzione del compilatore per avvisarlo e attivarlo.

Una cosa che molti programmatori sostengono è l'attivazione dell'opzione "Tratta gli avvisi come errori" (-Werror) per impedire il completamento dei build con successo se ci sono avvisi.


Grazie per la risposta! Sono d'accordo con -Werror. In realtà, come sto usando Clang, ho impostato tutti questi avvertimenti utilizzando pragma (#pragma clang diagnostica), e sono impostati a errori fatali, quindi questo è lo stesso -Werror:)
Macmade

0

Penso che la prova migliore che le persone si preoccupino degli avvertimenti sia il fatto che esistono e sono ampiamente utilizzati. Sono fermamente convinto che più errori vengono rilevati durante la compilazione, meglio è. Se questa non fosse una convinzione diffusa, tutti userebbero linguaggi deboli tipizzati in modo dinamico, perché i loro compilatori o interpreti ti offrono molto più margine di manovra. Al contrario, anche nei linguaggi di scripting l'uso delle strictbandiere è popolare. Su linguaggi statici fortemente tipizzati non solo le persone usano -Wall -Werrorfrequentemente, ma spesso trovano avvisi così preziosi da desiderare ancora di più , quindi pagano per strumenti di analisi statica come Coverity il cui unico scopo è fornire avvisi.

Ciò non significa che non ci siano detrattori. Molti sviluppatori vedono gli avvisi che lavorano contro di loro a breve termine piuttosto che lavorare per loro a lungo termine e non cercano di risolvere il problema di fondo. Quindi, vedi un codice adorabile come questo frammento che mi sono imbattuto ieri:

node = node; // Shut up compiler warning

Grazie per la tua risposta. Il problema è che spesso vedo persone che lavorano senza -Werror, e in realtà ignorano gli avvisi emessi dal compilatore. O se lo fanno, di solito si limitano a -Wall. La maggior parte delle bandiere che fornisco nella mia domanda non sono coperte -Walle personalmente ritengo che siano anche essenziali. Quindi sono solo paranoico? ; )
Macmade il

0

In generale, mi rivolgo di più verso -Wall, e sicuramente credo anche nell'includere -Werror. Un modulo non dovrebbe mai prevedere avvisi. Alla fine riceverai un altro avviso e lo perderai. E quello sarà quello che in realtà è un problema.

Dipende anche se stai aggiungendo "-Wall -Werror" a un progetto nuovo o vecchio. Se è nuovo, allora cercalo e richiedi la perfezione, altrimenti probabilmente rimani per giorni o settimane di modifiche e test. Ho appena fatto questo su un progetto un po 'più vecchio e ho trascorso due o tre giorni a rimuovere gli avvisi. Penso che il codice sia più pulito ora.

In altre parole, provalo e vedi.

lascivo


1
Se sei solo interessato da -Walle -Werror, suppongo che dovresti rileggere la domanda. Molti flag di avvertimento non sono coperti da -Wall. Grazie per la risposta, comunque.
Macmade il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.