2016-06-08 11 views
7

rozważyć następujący kod:W jaki sposób mogę wykryć martwy kod po metodach zawsze rzucających?

@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"); 
} 

Dla mnie wydaje się absolutnie niemożliwe, że println() zostanie kiedykolwiek zrealizowany - jako wezwanie do letsThrow() będzie zawsze wyjątek.

Dlatego jestem

a) zaskoczony, że kompilator nie może mi powiedzieć „to jest martwy kod”

b) zastanawiać, czy istnieją pewne flagi kompilatora (lub eclipse ustawienia), które mogłyby spowodować mówiąc: masz tam martwy kod.

+1

Wyobraź sobie tę samą sytuację, ale z dużo bardziej złożonym kodem w metodzie 'letsThrow()' z dużą ilością wewnętrznych wywołań metod. Czy prosisz kompilatora, aby również sprawdził martwy kod w tej sytuacji? Kompilator nie jest wszechmocny. – SimY4

+0

Najlepszym sposobem na poznanie jest dokładne sprawdzenie jednostki i sprawdzenie zasięgu kodu koniczyny, która powie Ci, który kod nigdy nie jest wykonywany. – Kode

+0

@Vwin Niezupełnie. Kod, który spowodował, że pytam wygląda na "catch (Whatever w) {tuneAndRethrow (w); rzucać Bla (w); } 'więc istnieje tylko jedna linia martwego kodu. To oznaczałoby, że potrzebuję prawie 100% pokrycia dla wszystkich naszych klas, aby znaleźć te miejsca. Nie brzmi dla mnie jak prawdziwy plan. – GhostCat

Odpowiedz

4

Błędy w czasie kompilacji martwego kodu są określane przez kompilator, a nie przez IDE. Chociaż jest prawdą, kod nigdy nie zostanie wykonany, nie narusza żadnych zasad dotyczących nieosiągalnych instrukcji z Dokumentów Oracle.

Od Unreachable Statements

Ten rozdział poświęcony jest dokładnym wyjaśnieniem słowa „osiągalny”. Chodzi o to, że musi istnieć jakaś ścieżka wykonywania od początku konstruktora, metody, inicjalizatora instancji lub inicjalizatora statycznego, który zawiera instrukcję do samej instrukcji. Analiza uwzględnia strukturę oświadczeń. Poza specjalnym traktowaniem while, do i dla instrukcji, których warunek ma stałą wartość true, wartości wyrażeń nie są brane pod uwagę w analizie przepływu.

Zasady obowiązujące w tym przypadku zależą od tego, czy bloki, które utworzyłeś, są osiągalne. (iff = if i tylko jeśli)

Pusty blok, który nie jest blokiem przełączającym, można zakończyć normalnie, jeśli jest on osiągalny.

Niepusty blok, który nie jest blokiem przełączającym, może zakończyć się normalnie, a ostatnia instrukcja w nim może zakończyć się normalnie.

Pierwsza instrukcja w niepustym bloku, który nie jest blokiem przełączników, jest osiągalna, jeśli blok jest osiągalny.

Każda inna instrukcja S w niepustym bloku, który nie jest blokiem przełączającym, jest dostępna iff instrukcja poprzedzająca S może zakończyć się normalnie.

Sposób letsThrow spełnia kryteria dla bloku roboczego kodu i technicznie uzupełnia normalnie. Zgłasza wyjątek, ale kończy. To, czy zgłasza wyjątek gwarantowany, nie jest brane pod uwagę przy ustalaniu, czy ten blok kodu w jego rzeczywistym użytkowaniu, , czy jest dostępny, czy nie, może zostać osiągnięty. W większości przypadków, martwy kod zostanie znaleziony tylko wtedy, gdy będzie zawierał try/catch/return, które stanowią większość reguł.

Rozważmy następujące, nawet bardziej zwięzły wersja:

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

Nie ma prawdziwego Licznik ten oprócz starannego użycia narzędzi pokrycia, ale jasne strony w przykładzie zobaczysz gwarantowane wyjątków za każdym razem uruchamiasz kod.

0

Celem jest przeprowadzenie kompleksowych testów jednostkowych i pomiary zasięgu testu. Martwy kod będzie widoczny, ponieważ żaden z twoich testów nie spowoduje jego wykonania.

+0

Zobacz mój komentarz do komentarza Vwins powyżej. – GhostCat

0

Deklarują swoją metodę jako powrót typ Throwable:

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

Następnie można wyrzucić, gdy nazywają go:

throw letsThrow(); 

Teraz każdy kod, który następuje wywołanie letsThrow() będzie wykrywany jako nie żyje.

Można to wymusić, sprawdzając przypadki, w których zwracana wartość letsThrow() nie jest używana przy użyciu narzędzia do analizy statycznej. Na przykład Google's errorprone has a checker for the @CheckReturnValue annotation, który zapewnia, że ​​używasz wyniku.

(W przypadku wersji dla biednych, wyszukaj wyrażenie regularne ^\s*letsThrow();).

+0

Interesujący pomysł. Ale myślę, że wolałbym, żeby 'letsThrow()' nie rzucał się w ogóle - cokolwiek innego wydaje się pytać o "metryczny wskaźnik jakości wtfodu" ... – GhostCat

+0

Nie rozumiem - jeśli nie chcesz 'letsThrow() 'nie rzucać, dlaczego to robi? –

+1

Mam na myśli: tylko "rzut wewnętrzny" rzeczywiście się wydarzy. Ale musisz zrozumieć tę metodę. Wydaje się, że wprowadzając w błąd czytelnika, należy napisać 'throw someCall()' ... jeśli już wiesz, że someCall() sam rzuci. Rozumiem, że ta konstrukcja rozwiązuje mój problem; ale jeśli pójdę na to, wolałbym przerobić 'letsThrow()', aby tylko wrócić, a nie rzucać! – GhostCat

Powiązane problemy