2016-06-08 9 views
7

consideri il seguente codice:Come posso rilevare il codice morto dopo i metodi di lancio sempre?

@Test 
public void testDeadCode() { 
    letsThrow(); 
    System.out.println("will never be reached"); 
} 

private final void letsThrow() { 
    throw new RuntimeException("guess you didnt see this one coming"); 
} 

a me sembra assolutamente impossibile che il println() avrebbe mai essere eseguito - come la chiamata a letsThrow() sarà sempre un'eccezione.

Così io sono

a) ha sorpreso che il compilatore non mi può dire "questo è il codice morto"

b) chiedendo se ci sono alcuni flag di compilazione (o eclissi impostazioni) che si tradurrebbe in dicendomi: hai il codice morto lì.

+1

Immaginate la stessa situazione ma con codice molto più complesso nel metodo 'letsThrow()' con molte chiamate di metodo interne. Chiedete al compilatore di controllare il codice morto in questa situazione anche per voi? Il compilatore non è onnipotente. – SimY4

+0

Il modo migliore per sapere è un completo test delle unità e il controllo sulla copertura del codice clover che ti dirà quale codice non viene mai eseguito – Kode

+0

@Vwin Non proprio. Il codice che mi ha fatto chiedere sembra 'catch (Whatever w) {tuneAndRethrow (w); lanciare Bla (w); } 'quindi c'è solo una riga di codice morto. Ciò significa che ho bisogno di una copertura vicina al 100% per tutte le nostre classi per trovare quei posti. Non sembra un vero piano per me. – GhostCat

risposta

4

Gli errori di compilazione in codice morto sono definiti dal compilatore e non dall'ID. Mentre è vero, il codice non verrà mai eseguito, non viola nessuna delle regole per le dichiarazioni non raggiungibili da Oracle Docs.

Da Unreachable Statements

Questa sezione è dedicata ad una spiegazione precisa della parola "raggiungibile". L'idea è che ci deve essere un possibile percorso di esecuzione dall'inizio del costruttore, del metodo, dell'inizializzatore di istanze o dell'inizializzatore statico che contiene l'istruzione nell'istruzione stessa. L'analisi tiene conto della struttura delle affermazioni. Fatta eccezione per il trattamento speciale di while, do e per le istruzioni la cui espressione condizionale ha il valore costante true, i valori delle espressioni non vengono presi in considerazione nell'analisi del flusso.

Le regole specifiche per questo caso sono relative al fatto se i blocchi che hai creato siano raggiungibili o meno. (iff = if and only if)

Un blocco vuoto che non è un blocco di interruttori può essere completato normalmente se è raggiungibile.

Un blocco non vuoto che non è un blocco di interruzione può essere completato normalmente se l'ultima istruzione in esso può essere completata normalmente.

La prima istruzione in un blocco non vuoto che non è un blocco di commutazione è raggiungibile se il blocco è raggiungibile.

Ogni altra istruzione S in un blocco non vuoto che non è un blocco di commutazione è raggiungibile se l'istruzione che precede S può essere completata normalmente.

Procedimento letsThrow soddisfa i criteri per un blocco di lavoro di codice e tecnicamente completa normalmente. Genera un'eccezione, ma completa. Indipendentemente dal fatto che lanci o meno un'eccezione garantita non viene presa in considerazione nel determinare se quel blocco di codice nel suo uso effettivo, , solo se è possibile o meno raggiungibile. Nella maggior parte dei casi, il codice morto viene rilevato solo quando include try/catch/returns, che sono la maggior parte delle regole.

Si consideri il seguente, ancora più conciso versione:

@Test 
public void testDeadCode() { 
    System.exit(0); 
    System.out.println("will never be reached"); 
} 

Non c'è nessun vero contatore per questo parte da un uso diligente di strumenti di copertura, ma il lato positivo nel tuo esempio è vedrete eccezioni garantiti ogni volta tu esegui il codice.

0

Scopo di avere test unitari completi e misurare la copertura del test del test. Il codice morto sarà visibile perché nessuno dei tuoi test ne causa l'esecuzione.

+0

Vedere il mio commento al commento di Vwins sopra. – GhostCat

0

Dichiarare il metodo in modo da restituire un tipo Throwable:

private final RuntimeException letsThrow() { 
    throw new RuntimeException("guess you didnt see this one coming"); 
} 

Poi si può buttare quando si chiama:

throw letsThrow(); 

Ora, qualsiasi codice che segue la chiamata alla letsThrow() sarà rilevabile come morto.

È possibile far rispettare ciò verificando i casi in cui il valore di ritorno di letsThrow() non viene utilizzato utilizzando lo strumento di analisi statico. Ad esempio, Google's errorprone has a checker for the @CheckReturnValue annotation che garantisce l'utilizzo del risultato.

(Per la versione di un uomo povero, cercare la regex ^\s*letsThrow();).

+0

Idea interessante. Ma suppongo che preferirei quindi 'letsThrow()' per non gettare affatto - qualsiasi altra cosa sembra chiedere un numero di "metrica di qualità del codice wtf" ... – GhostCat

+0

Non capisco - se non vuoi 'letsThrow() 'Non gettare, perché lo fa? –

+1

Intendo: solo il "tiro interno" avverrà effettivamente. Ma hai bisogno di immergerti in quel metodo per capirlo. Sembra fuorviante il lettore scrivere 'gettare someCall()' ... se già sai che qualche Call() stesso getterà. Capisco che questo costrutto risolve il mio problema; ma se me lo concedo, preferirei rielaborare 'letsThrow()' per tornare solo e non buttare! – GhostCat