Skip to main content

lisette_diagnostics/
pattern.rs

1use crate::LisetteDiagnostic;
2use syntax::ast::Span;
3
4use crate::IssueKind;
5
6#[derive(Debug, Clone)]
7pub struct PatternIssue {
8    pub span: Span,
9    pub kind: IssueKind,
10}
11
12pub fn non_exhaustive(match_span: Span, case: &str) -> LisetteDiagnostic {
13    LisetteDiagnostic::error("`match` is not exhaustive")
14        .with_infer_code("non_exhaustive")
15        .with_span_label(&match_span, "not all patterns covered")
16        .with_help(format!(
17            "Handle the missing case `{}`, e.g. `{} => {{ ... }}`",
18            case, case
19        ))
20}
21
22pub fn irrefutable_while_let(pattern_span: Span) -> LisetteDiagnostic {
23    LisetteDiagnostic::error("Pattern always matches")
24        .with_infer_code("irrefutable_while_let")
25        .with_span_label(&pattern_span, "always matches")
26        .with_help("Use `loop` with `let` binding instead")
27}
28
29pub fn redundant_arm(
30    span: Span,
31    label: impl Into<String>,
32    help: impl Into<String>,
33) -> LisetteDiagnostic {
34    LisetteDiagnostic::error("Unreachable pattern")
35        .with_infer_code("redundant_arm")
36        .with_span_label(&span, label)
37        .with_help(help)
38}
39
40pub fn refutable_pattern(
41    pattern_span: Span,
42    witness: &str,
43    slice_info: Option<(usize, bool)>,
44) -> LisetteDiagnostic {
45    let label = describe_pattern_expectation(witness, slice_info);
46    let help = build_refutability_help(witness);
47
48    LisetteDiagnostic::error("Pattern might not match")
49        .with_infer_code("refutable_pattern")
50        .with_span_label(&pattern_span, label)
51        .with_help(help)
52}
53
54fn describe_pattern_expectation(witness: &str, slice_info: Option<(usize, bool)>) -> String {
55    if witness.starts_with('[') {
56        if let Some((len, has_rest)) = slice_info {
57            if has_rest {
58                return format!("only matches {} or more elements", len);
59            }
60            let word = if len == 1 { "element" } else { "elements" };
61            return format!("only matches {} {}", len, word);
62        }
63
64        return "only matches specific length".to_string();
65    }
66
67    if witness.contains("None") {
68        return "only matches `Some`".to_string();
69    }
70
71    if witness.contains("Err") {
72        return "only matches `Ok`".to_string();
73    }
74
75    format!("does not match `{}`", witness)
76}
77
78fn build_refutability_help(witness: &str) -> String {
79    if witness.starts_with('[') {
80        return r#"Use `match` to handle slices of any length:
81    match slice {
82        [a, b] => ...,
83        _ => ...,
84    }"#
85        .to_string();
86    }
87
88    if witness.contains("None") {
89        return r#"Use `if let` to handle only `Some`:
90    if let Some(x) = opt {
91        ...
92    }
93Or use `match` to also handle `None`:
94    match opt {
95        Some(x) => ...,
96        None => ...,
97    }"#
98        .to_string();
99    }
100
101    if witness.contains("Err") {
102        return r#"Use `if let` to handle only `Ok`:
103    if let Ok(x) = result {
104        ...
105    }
106Or use `match` to also handle `Err`:
107    match result {
108        Ok(x) => ...,
109        Err(e) => ...,
110    }"#
111        .to_string();
112    }
113
114    format!(
115        "Use `match` to handle all cases:\n    match value {{\n        {} => ...,\n        _ => ...,\n    }}",
116        witness
117    )
118}