JustDummies

Just dummies — but seriously powerful ones.

Documentation sections

JD017: EnumUniverseViolation

PropertyValue
CategoryConstraints (JustDummies.Constraints)
Severity🟠 Warning
Enabled by defaultYes

Any.Enum<TEnum>() only ever yields a value TEnum defines: one of its declared members, or — on a [Flags] enum, where OneOf accepts a combination on its own account — an OR of declared members. It never invents an undeclared numeric value, even though the CLR would let you write one.

Naming a value outside that is not a narrowing that happens to be empty, it is a category error. An exclusion that removes every value left to draw is the same error from the other side.

Noncompliant

Any.Enum<Permissions>().OneOf((Permissions)16)   // no OR of declared members produces 16
Any.Enum<Day>().OneOf((Day)99)                   // not a declared member at all
Any.Enum<Day>().OneOf((Day)3)                    // Day never said its members combine
Any.Enum<Day>().Except(Day.Mon, Day.Tue)         // nothing remains to draw

Compliant

Any.Enum<Permissions>().OneOf(Permissions.Read | Permissions.Write)   // writing a combination is asking for one
Any.Enum<Permissions>().AllowingCombinations()                        // a plain draw ranges over them once asked
Any.Enum<Permissions>().OneOf(Permissions.Read, Permissions.Write)
Any.Enum<Day>().Except(Day.Mon)

Why it is separate from the other constraint rules

Its domain is metadata — what the type declares — rather than interval arithmetic, and its mistake has its own teachable model: a value the type does not define is not a narrowing that happens to be empty, it is a category error. That is also what tells the rule where to stop. [Flags] is metadata saying the members combine, so Read | Write is a value the type defines without ever declaring it — and the rule follows the generator there rather than reading “declared” off the member list alone.

What it does not flag

  • A combination of declared members named in OneOf on a [Flags] enum — with AllowingCombinations(), without it, in either order. An allow-list is the pool, so writing a combination in one is asking for that exact value, and the generator draws it.
  • Any constraint once AllowingCombinations() is declared — including an exclusion that removes every declared member. That widens the universe to the OR-closure of the declared members, which no longer matches a declared value one for one, so the rule stands down rather than approximate it: Except(Read, Write) beside it still leaves Read | Write to draw, and the generator does draw it.
  • An exclusion that removes every declared member while the allow-list names a value none of the exclusions carries. What decides is whether anything the caller allowed survives, not whether the declared set was emptied.
  • An exclusion that removes every declared member while the allow-list holds an entry the rule cannot read — a variable, a field, anything but a constant. An allow-list is the pool, so an unreadable entry is a pool the rule cannot decide rather than an absent one, and on a [Flags] enum it may hold a combination no exclusion names.
  • A generic helper whose type argument is an unsubstituted type parameter — there is no enum to reason about, and the rule bails rather than guess.
  • A non-constant value, or a partial exclusion that leaves at least one member.
  • A conflict-asserting negative test.

Read the source, or correct it there opens in a new tab· Mirrored from lib-v1.0.0-preview.6