Why C# pattern matching is not exhaustive for enums?

Viewed 5068

Say, I have the following enum and the code testing enum:

enum Flag
{
    On,
    Off
}

string GetMessage(Flag flag) =>
    flag switch
    {
        Flag.On  => "State is ON",
        Flag.Off => "State is OFF"
    };

However, I get the warning:

Warning CS8509 The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(ConsoleApp.Flag)2' is not covered.

Why it's not exhaustive when I listed all enum's values? And what is (ConsoleApp.Flg)2 enum value?

2 Answers

Counterexample:

string Foo()
{
    return GetMessage((Flag)42);
}

Unfortunately C# enums are not as robust as algebraic data types (or variant types, however you like to call them) in Haskell or other languages with better FP features. It's really just some metadata around an integral numeric value (int by default), so there's nothing in the type system stopping you from passing a value that does not correspond to a valid enum value. The compiler tells you just that, using (Flag)2 as a possible value. To fix the issue, add a standard catch-all:

string GetMessage(Flag flag) =>
    flag switch
    {
        Flag.On  => "State is ON",
        Flag.Off => "State is OFF",
        _        => throw new ArgumentOutOfRangeException(nameof(flag)),
    };

Good news! In recent versions of the Roslyn compiler, this warning (where, for example, the pattern (ConsoleApp.Flag)2 is not covered) has been given a new code CS8524.

The original warning code CS8509 now applies only to missing named enum values.

So we can now tell the compiler to ignore CS8524 where we deem it unnecessary code bloat to write a catch-all handler for unnamed enum values but still want to catch cases where we forgot to handle a named value (or we add new named values to an existing enum).

Also, if previously we told the compiler to ignore CS8509 to avoid writing _ => throw ... handlers, we might want to change that to ignore CS8524 instead now so we get back our CS8509 warning for the cases we do want warnings about!

Background: The Roslyn change was made in dotnet/roslyn#47066 which I discovered when reading the comments for dotnet/csharplang#2671 (Exhaustability for switch expressions on enums should be less strict).

Related