In che modo i compilatori dovrebbero segnalare errori e avvisi?

11

Non ho intenzione di scrivere un compilatore nel prossimo futuro; tuttavia, sono abbastanza interessato alle tecnologie del compilatore e a come queste cose potrebbero essere migliorate.

A partire dai linguaggi compilati, la maggior parte dei compilatori ha due livelli di errore: avvertenze ed errori, il primo è il più delle volte non fatali da correggere e errori che indicano la maggior parte delle volte che è impossibile produrre macchine (o byte-) codice dall'input.

Tuttavia, questa è una definizione piuttosto debole. In alcuni linguaggi come Java, alcuni avvisi sono semplicemente impossibili da eliminare senza utilizzare la direttiva @SuppressWarning . Inoltre, Java considera alcuni errori non fatali come errori (ad esempio, il codice non raggiungibile in Java genera un errore per una ragione che mi piacerebbe conoscere).

C # non ha gli stessi problemi, ma ne ha alcuni. Sembra che la compilazione avvenga in più passaggi e un errore di mancata gestione impedirà l'esecuzione degli ulteriori passaggi. Per questo motivo, il conteggio degli errori che si ottiene quando la build fallisce è spesso grossolanamente sottostimato. In una corsa potresti dire che hai due errori, ma una volta risolti, ne riceverai 26 nuovi.

Il passaggio a C e C ++ mostra semplicemente una cattiva combinazione sulle debolezze diagnostiche della compilazione di Java e C # (anche se potrebbe essere più accurato dire che Java e C # andavano a testa a mano con metà dei problemi ciascuno). Alcuni avvertimenti dovrebbero essere veramente errori (ad esempio quando non tutti i percorsi di codice restituiscono un valore) e tuttavia sono avvertimenti perché, suppongo, nel momento in cui hanno scritto lo standard, la tecnologia del compilatore non era abbastanza buona da rendere questo tipo di controlli obbligatori Allo stesso modo, i compilatori spesso controllano più di quanto dice lo standard, ma usano ancora il livello di errore di avviso "standard" per i risultati aggiuntivi. E spesso, i compilatori non segnaleranno tutti gli errori che potrebbero trovare subito; potrebbe richiedere un paio di compilazioni per sbarazzarsi di tutti loro. Per non parlare degli errori criptici che i compilatori C ++ amano sputare, in cui un singolo errore può causare decine di messaggi di errore.

Ora aggiungendo che molti sistemi di compilazione sono configurabili per segnalare errori quando i compilatori emettono avvisi, otteniamo solo uno strano mix: non tutti gli errori sono fatali, ma alcuni avvertimenti dovrebbero; non tutti gli avvertimenti sono meritati, ma alcuni sono esplicitamente soppressi senza ulteriore menzione della loro esistenza; e a volte tutti gli avvisi diventano errori.

Le lingue non compilate hanno ancora la loro parte di segnalazione di errori di merda. I tentativi di digitazione in Python non verranno segnalati fino a quando il codice non verrà effettivamente eseguito e non potrai mai kickare più di un errore alla volta perché lo script smetterà di essere eseguito dopo che ne ha incontrato uno.

PHP, da parte sua, ha un sacco di livelli di errore più o meno significativi, eccezioni e . Gli errori di Parse sono segnalati uno alla volta, gli avvertimenti sono spesso così brutti che dovrebbero abortire il tuo script (ma non di default), le avvertenze mostrano spesso gravi problemi logici, alcuni errori non sono abbastanza cattivi per fermare il tuo script ma comunque e, come al solito con PHP, ci sono alcune cose davvero strane laggiù (perché diavolo abbiamo bisogno di un livello di errore per errori fatali che non sono davvero fatali? E_RECOVERABLE_E_ERROR , sto parlando con te).

Mi sembra che ogni singola implementazione del report di errore del compilatore a cui riesco a pensare sia interrotta. Il che è un vero peccato, dal momento che tutti i bravi programmatori insistono sull'importanza di trattare correttamente gli errori e tuttavia non possono ottenere i propri strumenti per farlo.

Quale pensi che dovrebbe essere il modo giusto per segnalare gli errori del compilatore?

    
posta zneak 25.01.2011 - 01:25
fonte

2 risposte

5

Sembra che la tua domanda non riguardi in realtà in che modo riportiamo errori del compilatore, ma riguarda la classificazione dei problemi e cosa fare su di essi.

Se iniziamo assumendo, per il momento, che la dicotomia avvertenza / errore sia corretta, vediamo come possiamo costruire su questo. Alcune idee:

  1. Diversi "livelli" di avviso. Molti compilatori ordinano di implementarlo (per esempio GCC ha molti switch per configurare esattamente cosa avviserà), ma ha bisogno di lavoro - per esempio, riportando la gravità di un avviso segnalato e la possibilità di impostare "avvisi" sono errori "solo per avvertimenti superiori a una severità specificata.

  2. Classificazione sana di errori e avvertenze. Un errore dovrebbe essere segnalato solo se il codice non soddisfa le specifiche e quindi non può essere compilato. Le istruzioni irraggiungibili, anche se probabilmente un errore di codifica, dovrebbero essere un avviso , non un errore - il codice è ancora "valido" e ci sono istanze legittime in cui si vorrebbe compilare con codice irraggiungibile (veloce modifiche per il debug, per esempio).

Ora le cose su cui non sono d'accordo:

  1. Fare uno sforzo in più per segnalare ogni problema. Se c'è un errore, questo rompe la build. La build è rotta. La build non funzionerà fino a quando l'errore non verrà risolto. Quindi, è meglio segnalare immediatamente questo errore, piuttosto che "continuare" per cercare di identificare tutto il resto "sbagliato" con il codice. Soprattutto quando molte di queste cose sono probabilmente causate comunque dall'errore iniziale.

  2. Il tuo esempio specifico di un avviso-che-dovrebbe-essere-un-errore. Sì, probabilmente è un errore programmatore. No, non dovrebbe rompere la build. Se so che l'input della funzione è tale che restituirà sempre un valore, dovrei essere in grado di eseguire la build e fare alcuni test senza dover aggiungere quei controlli extra. Sì, dovrebbe essere un avvertimento. E uno maledettamente severo in questo. Ma non dovrebbe rompere la build in sé e per sé, a meno che non compili con warnings-are-errors.

Pensieri?

    
risposta data 25.01.2011 - 01:48
fonte
7

Un problema sollevato era la segnalazione incompleta degli errori, ad esempio la segnalazione di 2 errori e, quando li correggi, ne ottieni altri ancora.

Questo è (in gran parte) un compromesso da parte dello scrittore di compilatori. A seconda dell'errore che hai commesso, è molto facile che il compilatore inizi a fraintendere ciò che fai ha abbastanza male da iniziare a segnalare errori che hanno poco a che fare con la realtà. Ad esempio, prendi in considerazione un semplice refuso in cui hai qualcosa come itn x; invece di int x; . A meno che tu non abbia fatto qualcos'altro che rende itn significhi qualcosa, questo verrà segnalato come un errore. Va benissimo, ma ora considera cosa succederà dopo - il compilatore guarda un sacco di codice che tenta di utilizzare x come variabile. Dovrebbe A) fermarsi e lasciarti aggiustare, oppure B) sputare 2000 errori su error: "x": undeclared identifier o qualcosa su quell'ordine? Considera un'altra possibilità:

int main()[

Questo è un altro errore tipografico ovvio: ovviamente dovrebbe essere un { invece di un [ . Il compilatore può dirti quella parte abbastanza facilmente - ma dovrebbe poi continuare a segnalare un errore per qualcosa come x=1; che dice qualcosa come error: statement only allowed inside a function ?

Si noti che questi sono problemi anche abbastanza banali - quelli molto peggiori sono facili da trovare (specialmente, come molti di noi sanno, quando si entra in modelli C ++). La linea di fondo è che lo scrittore di compilatori di solito è bloccato nel tentativo di compromettere tra la segnalazione di errori falsi (ad esempio, segnalare qualcosa come un errore, anche se va bene) e non riuscire a riportare errori reali. Ci sono alcune regole del pollice più seguite per cercare di evitare di andare troppo male in entrambe le direzioni, ma quasi nessuno di loro è quasi perfetto.

Un altro problema che hai menzionato era Java e @SupressWarning . Questo è abbastanza diverso da quanto sopra - sarebbe abbastanza banale da risolvere. L'unica ragione per cui non è corretto è che farlo non si adatta al "personaggio" di base di Java, ad esempio, "non è un bug, è una caratteristica". Anche se di solito è uno scherzo, in questo caso le persone coinvolte sono così fuorvianti che credono davvero che sia vero.

Il problema che citi in C e C ++ con percorsi di codice che non restituiscono un valore non è in realtà quello di consentire ai compilatori primitivi. È per consentire decenni di codice esistenti, alcuni dei quali nessuno vuole risolvere, toccare o persino leggere. È antico e brutto ma funziona, e nessuno vuole altro che continuare a lavorare. Nel bene e nel male, i comitati linguistici sono praticamente bloccati nel mantenere quella compatibilità con le versioni precedenti, quindi continuano a consentire cose che nessuno piace davvero - ma alcune persone (almeno pensano di averle) hanno bisogno.

    
risposta data 25.01.2011 - 02:45
fonte

Leggi altre domande sui tag