Statement follows an unconditional terminator in the same block

ID

vbnet.correctness.unreachable_code

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Dead Code

Language

VB.NET

Description

Reports a statement that sits immediately after a statement which unconditionally leaves the block, so it can never run. The terminators recognised are Return, Throw, Exit Sub, Exit Function, Exit Property, Exit For, Exit While, Exit Do, Continue For, Continue While, Continue Do and GoTo. Only the statement directly following the terminator in the same block is reported, and the terminator has to be unconditional - a Return that is the last statement of an If block does not make the code after the End If unreachable and is not reported. Code that follows an Exit Sub but is introduced by a line label is a legitimate jump target reached through GoTo or On Error GoTo, and is not reported either - nor is a statement that carries a line label anywhere inside it, such as a dead If block holding a jump target, which is left to the reader rather than reported on a guess.

Rationale

Code that cannot run is almost never written on purpose, so a finding here usually means the procedure does not do what its author intended. The two common causes are both defects. Either a terminator was added above code that still has to run - an early Return inserted while debugging, or a Throw added to a validation branch that already had a fallback below it - in which case cleanup, logging, a counter update or a cache write is now silently skipped and the procedure returns having done only half its work. Or a second Return sits below the first, in which case the value the reader expects the procedure to produce is not the value it produces. Either way the compiler accepts the code, so nothing fails at build time, and no test can cover the dead lines, which is why this survives review: coverage reports show the lines as untested rather than as impossible, and the discrepancy is read as a missing test rather than as a bug.

The following code illustrates the pattern detected by this rule:

Public Function Discount(ByVal amount As Decimal) As Decimal
    ' FLAGGED: Statement follows an unconditional terminator in the same block
    Return amount * 0.9D
    Return amount
End Function

Remediation

Work out which of the two statements is the mistake, rather than deleting whichever is easier. If the code after the terminator still has to run, move it above the terminator - or, when it is cleanup that has to run on every exit path, move it into a Finally block or convert the resource to a Using block. If the terminator was meant to apply only in some cases, put it back under its condition, for example inside an If …​ Then Return guard. If the dead code is genuinely obsolete, delete it: leaving it in place tells the next reader it still runs. Enabling Option Strict On and treating the compiler’s unreachable-code warning as an error stops the next one from being committed.

Configuration

This detector does not need any configuration.