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}