Skip to content

test(core): ALL_KINDS can silently omit a ManifestKind variant while the suite stays green #124

Description

@justin13888

Came out of reviewing #102. ALL_KINDS (crates/dependable-core/src/manifest.rs, #[cfg(test)] mod tests) is the set that every kind-wide invariant is asserted over — only_cargo_looks_for_a_workspace_root and a_self_governing_kind_is_one_of_its_own_root_kinds both iterate it. If a ManifestKind variant is missing from the array, those invariants are silently skipped for that kind and the suite stays green.

Its guard was an exhaustive match kind { A | B | ... => {} } whose doc claimed a new variant "cannot be added and quietly skip every kind-wide invariant below". That is not what it enforced: the exhaustive match makes a new variant fail to compile, but the compiler's own suggested minimal fix — add the variant to the or-pattern — restores a green build while ALL_KINDS still holds one fewer kind than the enum has.

#102 replaced it with an index round trip (each arm names the kind's position; ALL_KINDS[position] == kind is asserted), which is strictly better — it catches duplication and misordering — and corrected the doc to stop overclaiming. It does not close the hole, and this was demonstrated rather than assumed:

  • Adding a 13th variant PackageSwift to ManifestKind breaks compilation in four places, ALL_KINDS' match among them.
  • Filling each with the minimal arm the compiler suggests (ManifestKind::PackageSwift => 12) compiles clean, and cargo test -p dependable-core manifest reports 15 passed; 0 failed with ALL_KINDS holding 12 of 13 variants.
  • The arm is never evaluated, because the loop iterates ALL_KINDS — which is exactly the thing missing the variant.

This is live rather than hypothetical: #84 adds PomXml and #85 adds PackageSwift. Both add new lines, so neither produces a textual conflict with the guard; whichever merges second must update the match arm, the array, and the [ManifestKind; 12] length annotation together, with nothing but review catching a miss.

Why it was not fixed

No dependency-free construction on stable Rust forces a variant into a hand-written array. std::mem::variant_count is unstable; strum::EnumCount/EnumIter would add a dependency (a dev-dependency would still change Cargo.lock, which the repair was scoped to avoid), and dependable-core is defined as the pure crate.

Suggested fix

Declare the enum and its test list from one source with a macro_rules! — zero dependencies, stable:

macro_rules! manifest_kinds {
    ($($variant:ident),* $(,)?) => {
        // ... enum attrs and docs ...
        pub enum ManifestKind { $($variant),* }

        #[cfg(test)]
        const ALL_KINDS: &[ManifestKind] = &[$(ManifestKind::$variant),*];
    };
}

A variant then cannot exist without appearing in ALL_KINDS, and the length annotation disappears. The cost is that the public enum is declared through a macro, which changes how it reads and how rustdoc presents it — worth weighing against the guarantee.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions