nvzqz/static-assertions

Assert an enum variant exists

nvzqz opened this issue · 1 comments

nvzqz commented

As per @robinkrahl's request (#2 (comment)), I made an attempt at this but it doesn't necessarily work. The only limited working form is if $v is an ident or $(tt)+ where only assert_variant!(Thing, A); works and neither of the others does. And even then, it still requires the #![feature(type_alias_enum_variants)] attribute for some reason on nightly.

Below is my current (failing) attempt at a solution:

#![feature(underscore_const_names)]
#![feature(type_alias_enum_variants)]

enum Thing {
    A,
    B { x: () },
    C(()),
}

macro_rules! assert_variant {
    ($e:ty, $v:pat) => {
        const _: fn($e) -> () = |e| match e {
            <$e>::$v => {},
            _ => {},
        };
    }
}

assert_variant!(Thing, A);
assert_variant!(Thing, B { .. });
assert_variant!(Thing, C(_));

fn main() {}

Ideally, the best solution would allow for:

assert_variant!(Thing, A);
assert_variant!(Thing, B);
assert_variant!(Thing, C);

assert_variant!(Thing, A, B, C);
Lej77 commented

I made an attempt at implementing this:

macro_rules! assert_variant {
    ($e:ty: $($v:ident),+ $(,)?) => {
        const _: fn($e) = |e| {
            type AnEnum = $e;
            $(
                if let AnEnum::$v { .. } = e {}
            )+
        };
    };
}

#[allow(dead_code)]
pub enum Thing {
    A,
    B { x: () },
    C(()),
    AnEnum,
}

fn main() {
    assert_variant!(Thing: A);
    assert_variant!(Thing: B);
    assert_variant!(Thing: C);
    
    assert_variant!(self::Thing: AnEnum, A, B, C);
}

old playground link (used re-exports via use but required $e:path instead of $e:ty in macro) (includes some tests)

playground link (includes some tests)