Skip to content

Exhaustive Enum Switch Coverage #91

Description

@blowin

Overview

  • Category: Good Practice (BCC3xxx)
  • Severity: Warning
  • Target: switch statements and switch expressions (SwitchStatementSyntax, SwitchExpressionSyntax)

Description

This rule ensures that when a switch block operates on an enum type expression, it explicitly handles every single defined value within that enumeration.

If any enum member is omitted from the switch labels, the analyzer triggers a warning. This forces developers to intentionally define the behavior for every possible enum state, significantly hardening the application against unhandled branch bugs.


Why is this rule needed?

Enumerations often expand over time as business requirements evolve. A developer might add a new status value (e.g., Suspended to a UserStatus enum) but forget to update the various switch structures scattered across the codebase that map or process that status.

Relying solely on a default: catch-all branch can obscure these omissions:

  1. Silent Fall-Through / Logic Bugs: If the default branch simply returns a generic value or log statement, the application fails silently or acts unpredictably when processing the new enum value.
  2. Defensive Programming: Forcing explicit coverage for every case means that as soon as a new enum value is added, the compiler/analyzer will immediately point out every single location in the code that needs to be updated to support it.

Code Examples

Non-Compliant

A new order status (Shipped) was added to the enum, but this switch statement was left un-updated, causing it to fall into an unintended or generic fallback block.

public enum OrderStatus { Pending, Processing, Shipped, Cancelled }

public void HandleOrder(OrderStatus status)
{
    // Violation: 'Shipped' is completely missing from the switch sections
    switch (status)
    {
        case OrderStatus.Pending:
            PreparePackage();
            break;
        case OrderStatus.Processing:
            ChargeCustomer();
            break;
        case OrderStatus.Cancelled:
            RestockItems();
            break;
    }
}

Compliant

Every enum value maps to its dedicated handling path. If a new member is added later, this block will immediately trigger a fresh BCC3008 warning until updated.

public void HandleOrder(OrderStatus status)
{
    switch (status)
    {
        case OrderStatus.Pending:
            PreparePackage();
            break;
        case OrderStatus.Processing:
            ChargeCustomer();
            break;
        case OrderStatus.Shipped:
            DispatchCourier();
            break;
        case OrderStatus.Cancelled:
            RestockItems();
            break;
        default:
            throw new ArgumentOutOfRangeException(nameof(status), status, null);
    }
}

Implementation Heuristic & Technical Tips (Roslyn API)

Analyzer Logic (DiagnosticAnalyzer)

  1. Register a Syntax Node Action targeting SyntaxKind.SwitchStatement and SyntaxKind.SwitchExpression.
  2. Use the SemanticModel to obtain the type information of the governing expression (switch (expression)). Verify that its ITypeSymbol has a TypeKind equal to TypeKind.Enum.
  3. Extract Declared Enum Values: Cast the symbol to INamedTypeSymbol and retrieve all fields that are enum constants (GetMembers().OfType<IFieldSymbol>()). Compile a list of their underlying constant values or names.
  4. Analyze Switch Labels:
    • For SwitchStatementSyntax, iterate through the Sections and analyze each CaseSwitchLabelSyntax.
    • For SwitchExpressionSyntax, iterate through the Arms.
    • Use the semantic model to resolve which enum constant symbol each label refers to.
  5. Compare and Match: Subtract the covered constants from the master list of declared enum fields.
  6. Apply Flags Exception: Check if the enum type is decorated with the [System.Flags] attribute. If it is a bitwise flag enum, abort the analysis, as exhaustive switch checks are mathematically unfeasible and inappropriate for bitwise combinations.
  7. If any standard enum value is completely omitted, report BCC3008 on the switch keyword token, listing the missing enum members in the diagnostic message.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions