Skip to main content

rustc_pattern_analysis/
errors.rs

1use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic};
2use rustc_macros::{Diagnostic, Subdiagnostic};
3use rustc_middle::ty::Ty;
4use rustc_span::Span;
5
6use crate::rustc::{RustcPatCtxt, WitnessPat};
7
8#[derive(const _: () =
    {
        impl rustc_errors::Subdiagnostic for Uncovered {
            fn add_to_diag<__G>(self, diag: &mut rustc_errors::Diag<'_, __G>)
                where __G: rustc_errors::EmissionGuarantee {
                match self {
                    Uncovered {
                        span: __binding_0,
                        count: __binding_1,
                        witness_1: __binding_2,
                        witness_2: __binding_3,
                        witness_3: __binding_4,
                        remainder: __binding_5 } => {
                        let mut sub_args = rustc_errors::DiagArgMap::default();
                        sub_args.insert("count".into(),
                            rustc_errors::IntoDiagArg::into_diag_arg(__binding_1,
                                &mut diag.long_ty_path));
                        sub_args.insert("witness_1".into(),
                            rustc_errors::IntoDiagArg::into_diag_arg(__binding_2,
                                &mut diag.long_ty_path));
                        sub_args.insert("witness_2".into(),
                            rustc_errors::IntoDiagArg::into_diag_arg(__binding_3,
                                &mut diag.long_ty_path));
                        sub_args.insert("witness_3".into(),
                            rustc_errors::IntoDiagArg::into_diag_arg(__binding_4,
                                &mut diag.long_ty_path));
                        sub_args.insert("remainder".into(),
                            rustc_errors::IntoDiagArg::into_diag_arg(__binding_5,
                                &mut diag.long_ty_path));
                        let __message =
                            rustc_errors::format_diag_message(&rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("{$count ->\n        [1] pattern `{$witness_1}`\n        [2] patterns `{$witness_1}` and `{$witness_2}`\n        [3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}`\n        *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more\n    } not covered")),
                                &sub_args);
                        diag.span_label(__binding_0, __message);
                    }
                }
            }
        }
    };Subdiagnostic)]
9#[label(
10    "{$count ->
11        [1] pattern `{$witness_1}`
12        [2] patterns `{$witness_1}` and `{$witness_2}`
13        [3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}`
14        *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more
15    } not covered"
16)]
17pub struct Uncovered {
18    #[primary_span]
19    span: Span,
20    count: usize,
21    witness_1: String, // a printed pattern
22    witness_2: String, // a printed pattern
23    witness_3: String, // a printed pattern
24    remainder: usize,
25}
26
27impl Uncovered {
28    pub fn new<'p, 'tcx>(
29        span: Span,
30        cx: &RustcPatCtxt<'p, 'tcx>,
31        witnesses: Vec<WitnessPat<'p, 'tcx>>,
32    ) -> Self
33    where
34        'tcx: 'p,
35    {
36        let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
37        Self {
38            span,
39            count: witnesses.len(),
40            // Substitute dummy values if witnesses is smaller than 3. These will never be read.
41            witness_2: witnesses.get(1).map(|w| cx.print_witness_pat(w)).unwrap_or_default(),
42            witness_3: witnesses.get(2).map(|w| cx.print_witness_pat(w)).unwrap_or_default(),
43            witness_1,
44            remainder: witnesses.len().saturating_sub(3),
45        }
46    }
47}
48
49#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            OverlappingRangeEndpoints where G: rustc_errors::EmissionGuarantee
            {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    OverlappingRangeEndpoints {
                        range: __binding_0, overlap: __binding_1 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("multiple patterns overlap on their endpoints")));
                        diag.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("you likely meant to write mutually exclusive ranges")));
                        ;
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("... with this range")));
                        for __binding_1 in __binding_1 {
                            diag.subdiagnostic(__binding_1);
                        }
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
50#[diag("multiple patterns overlap on their endpoints")]
51#[note("you likely meant to write mutually exclusive ranges")]
52pub struct OverlappingRangeEndpoints {
53    #[label("... with this range")]
54    pub range: Span,
55    #[subdiagnostic]
56    pub overlap: Vec<Overlap>,
57}
58
59#[derive(const _: () =
    {
        impl rustc_errors::Subdiagnostic for Overlap {
            fn add_to_diag<__G>(self, diag: &mut rustc_errors::Diag<'_, __G>)
                where __G: rustc_errors::EmissionGuarantee {
                match self {
                    Overlap { span: __binding_0, range: __binding_1 } => {
                        let mut sub_args = rustc_errors::DiagArgMap::default();
                        sub_args.insert("range".into(),
                            rustc_errors::IntoDiagArg::into_diag_arg(__binding_1,
                                &mut diag.long_ty_path));
                        let __message =
                            rustc_errors::format_diag_message(&rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("this range overlaps on `{$range}`...")),
                                &sub_args);
                        diag.span_label(__binding_0, __message);
                    }
                }
            }
        }
    };Subdiagnostic)]
60#[label("this range overlaps on `{$range}`...")]
61pub struct Overlap {
62    #[primary_span]
63    pub span: Span,
64    pub range: String, // a printed pattern
65}
66
67#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            ExclusiveRangeMissingMax where G: rustc_errors::EmissionGuarantee
            {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    ExclusiveRangeMissingMax {
                        first_range: __binding_0,
                        suggestion: __binding_1,
                        max: __binding_2 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("exclusive range missing `{$max}`")));
                        let __code_0 =
                            [::alloc::__export::must_use({
                                                ::alloc::fmt::format(format_args!("{0}", __binding_1))
                                            })].into_iter();
                        ;
                        diag.arg("suggestion", __binding_1);
                        diag.arg("max", __binding_2);
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("this range doesn't match `{$max}` because `..` is an exclusive range")));
                        diag.span_suggestions_with_style(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("use an inclusive range instead")),
                            __code_0, rustc_errors::Applicability::MaybeIncorrect,
                            rustc_errors::SuggestionStyle::ShowCode);
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
68#[diag("exclusive range missing `{$max}`")]
69pub struct ExclusiveRangeMissingMax {
70    #[label("this range doesn't match `{$max}` because `..` is an exclusive range")]
71    #[suggestion(
72        "use an inclusive range instead",
73        code = "{suggestion}",
74        applicability = "maybe-incorrect"
75    )]
76    /// This is an exclusive range that looks like `lo..max` (i.e. doesn't match `max`).
77    pub first_range: Span,
78    /// Suggest `lo..=max` instead.
79    pub suggestion: String,
80    pub max: String, // a printed pattern
81}
82
83#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            ExclusiveRangeMissingGap where G: rustc_errors::EmissionGuarantee
            {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    ExclusiveRangeMissingGap {
                        first_range: __binding_0,
                        gap: __binding_1,
                        suggestion: __binding_2,
                        gap_with: __binding_3 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("multiple ranges are one apart")));
                        let __code_1 =
                            [::alloc::__export::must_use({
                                                ::alloc::fmt::format(format_args!("{0}", __binding_2))
                                            })].into_iter();
                        ;
                        diag.arg("gap", __binding_1);
                        diag.arg("suggestion", __binding_2);
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("this range doesn't match `{$gap}` because `..` is an exclusive range")));
                        diag.span_suggestions_with_style(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("use an inclusive range instead")),
                            __code_1, rustc_errors::Applicability::MaybeIncorrect,
                            rustc_errors::SuggestionStyle::ShowCode);
                        for __binding_3 in __binding_3 {
                            diag.subdiagnostic(__binding_3);
                        }
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
84#[diag("multiple ranges are one apart")]
85pub struct ExclusiveRangeMissingGap {
86    #[label("this range doesn't match `{$gap}` because `..` is an exclusive range")]
87    #[suggestion(
88        "use an inclusive range instead",
89        code = "{suggestion}",
90        applicability = "maybe-incorrect"
91    )]
92    /// This is an exclusive range that looks like `lo..gap` (i.e. doesn't match `gap`).
93    pub first_range: Span,
94    pub gap: String, // a printed pattern
95    /// Suggest `lo..=gap` instead.
96    pub suggestion: String,
97    #[subdiagnostic]
98    /// All these ranges skipped over `gap` which we think is probably a mistake.
99    pub gap_with: Vec<GappedRange>,
100}
101
102pub struct GappedRange {
103    pub span: Span,
104    pub gap: String,         // a printed pattern
105    pub first_range: String, // a printed pattern
106}
107
108impl Subdiagnostic for GappedRange {
109    fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
110        let GappedRange { span, gap, first_range } = self;
111
112        // FIXME(mejrs) Use `#[subdiagnostic(eager)]` instead
113        let message = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this could appear to continue range `{0}`, but `{1}` isn\'t matched by either of them",
                first_range, gap))
    })format!(
114            "this could appear to continue range `{first_range}`, but `{gap}` isn't matched by \
115            either of them"
116        );
117        diag.span_label(span, message);
118    }
119}
120
121#[derive(const _: () =
    {
        impl<'_sess, 'tcx, G> rustc_errors::Diagnostic<'_sess, G> for
            NonExhaustiveOmittedPattern<'tcx> where
            G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    NonExhaustiveOmittedPattern {
                        scrut_ty: __binding_0, uncovered: __binding_1 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("some variants are not matched explicitly")));
                        diag.help(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("ensure that all variants are matched explicitly by adding the suggested match arms")));
                        diag.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found")));
                        ;
                        diag.arg("scrut_ty", __binding_0);
                        diag.subdiagnostic(__binding_1);
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
122#[diag("some variants are not matched explicitly")]
123#[help("ensure that all variants are matched explicitly by adding the suggested match arms")]
124#[note(
125    "the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found"
126)]
127pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
128    pub scrut_ty: Ty<'tcx>,
129    #[subdiagnostic]
130    pub uncovered: Uncovered,
131}
132
133#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            NonExhaustiveOmittedPatternLintOnArm where
            G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    NonExhaustiveOmittedPatternLintOnArm {
                        span: __binding_0,
                        lint_span: __binding_1,
                        suggest_lint_on_match: __binding_2,
                        lint_level: __binding_3,
                        lint_name: __binding_4 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the lint level must be set on the whole match")));
                        let __code_2 =
                            [::alloc::__export::must_use({
                                                ::alloc::fmt::format(format_args!("#[{0}({1})]\n",
                                                        __binding_3, __binding_4))
                                            })].into_iter();
                        diag.help(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("it no longer has any effect to set the lint level on an individual match arm")));
                        ;
                        diag.arg("lint_level", __binding_3);
                        diag.arg("lint_name", __binding_4);
                        diag.span(__binding_0);
                        diag.span_label(__binding_1,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("remove this attribute")));
                        if let Some(__binding_2) = __binding_2 {
                            diag.span_suggestions_with_style(__binding_2,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("set the lint level on the whole match")),
                                __code_2, rustc_errors::Applicability::MaybeIncorrect,
                                rustc_errors::SuggestionStyle::ShowCode);
                        }
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
134#[diag("the lint level must be set on the whole match")]
135#[help("it no longer has any effect to set the lint level on an individual match arm")]
136pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
137    #[primary_span]
138    pub span: Span,
139    #[label("remove this attribute")]
140    pub lint_span: Span,
141    #[suggestion(
142        "set the lint level on the whole match",
143        code = "#[{lint_level}({lint_name})]\n",
144        applicability = "maybe-incorrect"
145    )]
146    pub suggest_lint_on_match: Option<Span>,
147    pub lint_level: &'static str,
148    pub lint_name: &'static str,
149}
150
151#[derive(const _: () =
    {
        impl<'_sess, 'tcx, G> rustc_errors::Diagnostic<'_sess, G> for
            MixedDerefPatternConstructors<'tcx> where
            G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    MixedDerefPatternConstructors {
                        spans: __binding_0,
                        smart_pointer_ty: __binding_1,
                        deref_pattern_label: __binding_2,
                        normal_constructor_label: __binding_3 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("mix of deref patterns and normal constructors")));
                        ;
                        diag.arg("smart_pointer_ty", __binding_1);
                        diag.span(__binding_0.clone());
                        diag.span_label(__binding_2,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("matches on the result of dereferencing `{$smart_pointer_ty}`")));
                        diag.span_label(__binding_3,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("matches directly on `{$smart_pointer_ty}`")));
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
152#[diag("mix of deref patterns and normal constructors")]
153pub(crate) struct MixedDerefPatternConstructors<'tcx> {
154    #[primary_span]
155    pub spans: Vec<Span>,
156    pub smart_pointer_ty: Ty<'tcx>,
157    #[label("matches on the result of dereferencing `{$smart_pointer_ty}`")]
158    pub deref_pattern_label: Span,
159    #[label("matches directly on `{$smart_pointer_ty}`")]
160    pub normal_constructor_label: Span,
161}