Skip to main content

zelkova_compiler/
exhaustiveness.rs

1//! This module contains the exhaustiveness checker for pattern matching.
2//!
3//! The checker itself is not written yet: [`check`] accepts every module. What is
4//! here is the error it will report, and that is deliberate — an uninhabited
5//! `enum Error {}` made `check` unable to fail, which in turn made the conversion
6//! into a `CompilationError` a `todo!()` that nothing could reach and nobody had to
7//! justify. The first error the real checker reported would have panicked the
8//! compiler.
9use super::canonical::Module;
10use super::name::Name;
11use super::PhaseError;
12
13/// A `case` expression whose branches do not cover every variant of the type it
14/// scrutinises.
15///
16/// Nothing constructs this yet — [`check`] is a stub. It is defined now so that the
17/// phase has a renderable error before it has a checker, the same way
18/// `canonical::Error::InvalidTupleSize` is kept for a rejection path that does not
19/// exist yet. Writing the checker means constructing this, not inventing an error
20/// type and a diagnostic at the same time.
21#[derive(Debug, PartialEq)]
22pub enum Error {
23    NonExhaustiveMatch {
24        /// The value whose body holds the `case` expression.
25        value: Name,
26        /// The union type being matched on.
27        tpe: Name,
28        /// The variants of `tpe` that no branch matches.
29        missing: Vec<Name>,
30    },
31}
32
33impl PhaseError for Error {
34    fn message(&self) -> String {
35        match self {
36            Error::NonExhaustiveMatch { value, tpe, .. } => format!(
37                "the `case` expression in `{}` does not cover every variant of `{}`",
38                value, tpe
39            ),
40        }
41    }
42
43    fn notes(&self) -> Vec<String> {
44        match self {
45            Error::NonExhaustiveMatch { missing, .. } => vec![format!(
46                "no branch matches: {}",
47                missing
48                    .iter()
49                    .map(|n| n.to_string())
50                    .collect::<Vec<_>>()
51                    .join(", ")
52            )],
53        }
54    }
55}
56
57/// Verify that every `case` expression in `module` covers its scrutinee's variants.
58///
59/// Currently a stub: it inspects nothing and accepts every module. The
60/// `Vec<Error>` return is the accumulating shape the other phases use — when the
61/// checker is written, one uncovered `case` must not hide the next.
62pub fn check(_module: &Module) -> Result<(), Vec<Error>> {
63    Ok(())
64}