summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs
blob: a3aa2e4b389afe74f1e40f96da6fb5ea4eab66c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::macros::{is_panic, root_macro_call};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{is_wild, peel_blocks_with_stmt};
use rustc_hir::{Arm, Expr, PatKind};
use rustc_lint::LateContext;
use rustc_span::symbol::{kw, sym};

use super::MATCH_WILD_ERR_ARM;

pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
    let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
    if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
        for arm in arms {
            if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
                let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
                if path_str == "Err" {
                    let mut matching_wild = inner.iter().any(is_wild);
                    let mut ident_bind_name = kw::Underscore;
                    if !matching_wild {
                        // Looking for unused bindings (i.e.: `_e`)
                        for pat in inner.iter() {
                            if let PatKind::Binding(_, id, ident, None) = pat.kind {
                                if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
                                    ident_bind_name = ident.name;
                                    matching_wild = true;
                                }
                            }
                        }
                    }
                    if_chain! {
                        if matching_wild;
                        if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
                        if is_panic(cx, macro_call.def_id);
                        then {
                            // `Err(_)` or `Err(_e)` arm with `panic!` found
                            span_lint_and_note(cx,
                                MATCH_WILD_ERR_ARM,
                                arm.pat.span,
                                &format!("`Err({})` matches all errors", ident_bind_name),
                                None,
                                "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable",
                            );
                        }
                    }
                }
            }
        }
    }
}