]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::{ |
2 | indent_of, is_type_diagnostic_item, match_qpath, path_to_local_id, paths, reindent_multiline, snippet_opt, | |
3 | span_lint_and_sugg, | |
4 | }; | |
5 | use if_chain::if_chain; | |
6 | use rustc_errors::Applicability; | |
7 | use rustc_hir::{Expr, ExprKind, PatKind}; | |
8 | use rustc_lint::LintContext; | |
9 | use rustc_lint::{LateContext, LateLintPass}; | |
10 | use rustc_middle::lint::in_external_macro; | |
11 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
12 | use rustc_span::symbol::sym; | |
13 | ||
14 | declare_clippy_lint! { | |
15 | /// **What it does:** | |
16 | /// Finds patterns that reimplement `Option::ok_or`. | |
17 | /// | |
18 | /// **Why is this bad?** | |
19 | /// Concise code helps focusing on behavior instead of boilerplate. | |
20 | /// | |
21 | /// **Known problems:** None. | |
22 | /// | |
23 | /// **Examples:** | |
24 | /// ```rust | |
25 | /// let foo: Option<i32> = None; | |
26 | /// foo.map_or(Err("error"), |v| Ok(v)); | |
27 | /// ``` | |
28 | /// | |
29 | /// Use instead: | |
30 | /// ```rust | |
31 | /// let foo: Option<i32> = None; | |
32 | /// foo.ok_or("error"); | |
33 | /// ``` | |
34 | pub MANUAL_OK_OR, | |
35 | pedantic, | |
36 | "finds patterns that can be encoded more concisely with `Option::ok_or`" | |
37 | } | |
38 | ||
39 | declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); | |
40 | ||
41 | impl LateLintPass<'_> for ManualOkOr { | |
42 | fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { | |
43 | if in_external_macro(cx.sess(), scrutinee.span) { | |
44 | return; | |
45 | } | |
46 | ||
47 | if_chain! { | |
48 | if let ExprKind::MethodCall(method_segment, _, args, _) = scrutinee.kind; | |
49 | if method_segment.ident.name == sym!(map_or); | |
50 | if args.len() == 3; | |
51 | let method_receiver = &args[0]; | |
52 | let ty = cx.typeck_results().expr_ty(method_receiver); | |
53 | if is_type_diagnostic_item(cx, ty, sym::option_type); | |
54 | let or_expr = &args[1]; | |
55 | if is_ok_wrapping(cx, &args[2]); | |
56 | if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; | |
57 | if match_qpath(err_path, &paths::RESULT_ERR); | |
58 | if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span); | |
59 | if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); | |
60 | if let Some(indent) = indent_of(cx, scrutinee.span); | |
61 | then { | |
62 | let reindented_err_arg_snippet = | |
63 | reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); | |
64 | span_lint_and_sugg( | |
65 | cx, | |
66 | MANUAL_OK_OR, | |
67 | scrutinee.span, | |
68 | "this pattern reimplements `Option::ok_or`", | |
69 | "replace with", | |
70 | format!( | |
71 | "{}.ok_or({})", | |
72 | method_receiver_snippet, | |
73 | reindented_err_arg_snippet | |
74 | ), | |
75 | Applicability::MachineApplicable, | |
76 | ); | |
77 | } | |
78 | } | |
79 | } | |
80 | } | |
81 | ||
82 | fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { | |
83 | if let ExprKind::Path(ref qpath) = map_expr.kind { | |
84 | if match_qpath(qpath, &paths::RESULT_OK) { | |
85 | return true; | |
86 | } | |
87 | } | |
88 | if_chain! { | |
89 | if let ExprKind::Closure(_, _, body_id, ..) = map_expr.kind; | |
90 | let body = cx.tcx.hir().body(body_id); | |
91 | if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; | |
92 | if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; | |
93 | if match_qpath(ok_path, &paths::RESULT_OK); | |
94 | then { path_to_local_id(ok_arg, param_id) } else { false } | |
95 | } | |
96 | } |