Skip to main content

match_lang/
report.rs

1//! The result of checking a match.
2
3use alloc::vec::Vec;
4
5use crate::Witness;
6
7/// What checking a match found: missing cases, unreachable arms, and
8/// redundant or-pattern alternatives.
9///
10/// # Examples
11///
12/// ```
13/// use match_lang::{check, Arm, Model, Pat, Signature};
14///
15/// struct Flag;
16///
17/// impl Model for Flag {
18///     type Ty = ();
19///     fn signature(&self, _: &()) -> Signature<()> {
20///         Signature::Bool
21///     }
22///     fn fields(&self, _: &(), _: u32, _: &mut Vec<()>) {}
23/// }
24///
25/// let arms = [
26///     Arm::new(Pat::bool(true)),
27///     Arm::new(Pat::or([Pat::bool(false), Pat::bool(true)])),
28///     Arm::new(Pat::wild()),
29/// ];
30/// let report = check(&Flag, &(), &arms)?;
31/// assert!(report.is_exhaustive());
32/// assert_eq!(report.unreachable(), &[2]);
33/// assert_eq!(report.redundant()[0].arm, 1);
34/// assert_eq!(report.redundant()[0].node, 2); // the second alternative, `true`
35/// # Ok::<(), match_lang::Error>(())
36/// ```
37#[derive(Clone, Debug)]
38pub struct Report<T> {
39    pub(crate) missing: Vec<Witness<T>>,
40    pub(crate) unreachable: Vec<usize>,
41    pub(crate) redundant: Vec<Redundant>,
42    pub(crate) steps: u64,
43}
44
45impl<T> Report<T> {
46    /// Whether every value of the scrutinee's type is matched by some
47    /// unguarded arm.
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// # use match_lang::{check, Arm, Model, Pat, Signature};
53    /// # struct Flag;
54    /// # impl Model for Flag {
55    /// #     type Ty = ();
56    /// #     fn signature(&self, _: &()) -> Signature<()> { Signature::Bool }
57    /// #     fn fields(&self, _: &(), _: u32, _: &mut Vec<()>) {}
58    /// # }
59    /// let guarded = [Arm::guarded(Pat::wild())];
60    /// assert!(!check(&Flag, &(), &guarded)?.is_exhaustive());
61    /// # Ok::<(), match_lang::Error>(())
62    /// ```
63    #[must_use]
64    pub fn is_exhaustive(&self) -> bool {
65        self.missing.is_empty()
66    }
67
68    /// Witnesses of values no unguarded arm matches; empty exactly when the
69    /// match is exhaustive. There is at least one when it is not, and at most
70    /// the [witness limit](crate::Checker::with_witness_limit). They are a
71    /// sample chosen to be informative, not an enumeration of every missing
72    /// value.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// # use match_lang::{check, Arm, Model, Pat, Signature};
78    /// # struct Flag;
79    /// # impl Model for Flag {
80    /// #     type Ty = ();
81    /// #     fn signature(&self, _: &()) -> Signature<()> { Signature::Bool }
82    /// #     fn fields(&self, _: &(), _: u32, _: &mut Vec<()>) {}
83    /// # }
84    /// let report = check(&Flag, &(), &[])?;
85    /// let missing: Vec<String> = report.missing().iter().map(|w| w.to_string()).collect();
86    /// assert_eq!(missing, ["false", "true"]);
87    /// # Ok::<(), match_lang::Error>(())
88    /// ```
89    #[must_use]
90    pub fn missing(&self) -> &[Witness<T>] {
91        &self.missing
92    }
93
94    /// The indices of arms no value can reach, in ascending order. An arm is
95    /// unreachable when every value its pattern matches is matched by an
96    /// earlier unguarded arm, or when its pattern matches no value at all.
97    ///
98    /// # Examples
99    ///
100    /// ```
101    /// # use match_lang::{check, Arm, Model, Pat, Signature};
102    /// # struct Flag;
103    /// # impl Model for Flag {
104    /// #     type Ty = ();
105    /// #     fn signature(&self, _: &()) -> Signature<()> { Signature::Bool }
106    /// #     fn fields(&self, _: &(), _: u32, _: &mut Vec<()>) {}
107    /// # }
108    /// let arms = [Arm::new(Pat::wild()), Arm::new(Pat::bool(true))];
109    /// assert_eq!(check(&Flag, &(), &arms)?.unreachable(), &[1]);
110    /// # Ok::<(), match_lang::Error>(())
111    /// ```
112    #[must_use]
113    pub fn unreachable(&self) -> &[usize] {
114        &self.unreachable
115    }
116
117    /// Or-pattern alternatives that no value reaches, inside arms that are
118    /// themselves reachable, in arm order and then preorder. Only the
119    /// outermost redundant alternative is reported: alternatives nested
120    /// inside it are not listed again.
121    ///
122    /// An alternative is redundant when every value it matches is matched by
123    /// an earlier unguarded arm or, in an unguarded arm, by an earlier
124    /// alternative of the same arm.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// # use match_lang::{check, Arm, Model, Pat, Redundant, Signature};
130    /// # struct Flag;
131    /// # impl Model for Flag {
132    /// #     type Ty = ();
133    /// #     fn signature(&self, _: &()) -> Signature<()> { Signature::Bool }
134    /// #     fn fields(&self, _: &(), _: u32, _: &mut Vec<()>) {}
135    /// # }
136    /// // `true | false | true`: node 0 is the or-pattern, 1..=3 its alternatives.
137    /// let arms = [Arm::new(Pat::or([Pat::bool(true), Pat::bool(false), Pat::bool(true)]))];
138    /// let report = check(&Flag, &(), &arms)?;
139    /// assert_eq!(report.redundant(), &[Redundant { arm: 0, node: 3 }]);
140    /// # Ok::<(), match_lang::Error>(())
141    /// ```
142    #[must_use]
143    pub fn redundant(&self) -> &[Redundant] {
144        &self.redundant
145    }
146
147    /// The number of steps the check took, against the
148    /// [step limit](crate::Checker::with_step_limit). Useful to see how close
149    /// real matches come to the limit.
150    ///
151    /// # Examples
152    ///
153    /// ```
154    /// # use match_lang::{check, Arm, Model, Pat, Signature};
155    /// # struct Flag;
156    /// # impl Model for Flag {
157    /// #     type Ty = ();
158    /// #     fn signature(&self, _: &()) -> Signature<()> { Signature::Bool }
159    /// #     fn fields(&self, _: &(), _: u32, _: &mut Vec<()>) {}
160    /// # }
161    /// let report = check(&Flag, &(), &[Arm::new(Pat::wild())])?;
162    /// assert!(report.steps() > 0);
163    /// # Ok::<(), match_lang::Error>(())
164    /// ```
165    #[must_use]
166    pub fn steps(&self) -> u64 {
167        self.steps
168    }
169}
170
171/// A redundant or-pattern alternative: in arm `arm`, the alternative whose
172/// root is node `node` of the arm's pattern, by
173/// [preorder index](crate::Pat#node-numbering).
174///
175/// # Examples
176///
177/// ```
178/// use match_lang::Redundant;
179///
180/// let r = Redundant { arm: 2, node: 5 };
181/// assert_eq!((r.arm, r.node), (2, 5));
182/// ```
183#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
184pub struct Redundant {
185    /// The index of the arm.
186    pub arm: usize,
187    /// The preorder index of the alternative within the arm's pattern.
188    pub node: usize,
189}