Select Case statement nested inside another Select Case

ID

vbnet.maintainability.no_nested_switch

Severity

high

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Complexity

Language

VB.NET

Description

Reports a Select Case statement that contains another Select Case inside one of its Case or Case Else clauses, whether the inner statement is the entire body of the clause or follows other statements in it. Two Select Case statements written one after the other at the same level are not reported, and neither is an outer Select Case whose clauses delegate the second decision to a method that contains the inner Select Case.

Rationale

Two nested multi-way branches describe a decision over a pair of values, but they describe it in a shape where nothing keeps the pair complete. The number of paths through the statement is the product of the two case counts, and the only thing that ties an inner case to its outer case is where it happens to be written, so adding a value to either dimension leaves combinations silently unhandled - the compiler cannot help, because a Select Case with no matching clause is legal and simply does nothing. The nested form also hides which value each Case Else absorbs: a broad Case Else in the inner statement quietly covers new outer values as well, which is how a new status code ends up taking the fallback path that was written for a different situation.

The following code illustrates the pattern detected by this rule:

Public Function Route(ByVal kind As Integer, ByVal region As Integer) As String
    ' FLAGGED: Select Case statement nested inside another Select Case
    Select Case kind
        Case 1
            Select Case region
                Case 10
                    Return "domestic-express"
                Case Else
                    Return "domestic-standard"
            End Select
        Case Else
            Return "unknown"
    End Select
End Function

Remediation

Move the inner Select Case into its own procedure named after the decision it makes and take the inner value as its parameter, so the outer clause reads as a single step - Return RouteDomestic(region). When the two values really form one key, replace the pair with a single lookup keyed on both, for example a Dictionary from a tuple or composite key to the result, which makes the missing combinations visible as missing entries in one table. When the outer value selects behaviour rather than data, give each outer case its own type implementing a common interface and let the inner decision live in that type.

Configuration

This detector does not need any configuration.