]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
2 | use clippy_utils::method_chain_args; | |
3 | use clippy_utils::source::snippet_with_applicability; | |
4 | use clippy_utils::ty::is_type_diagnostic_item; | |
f20569fa XL |
5 | use if_chain::if_chain; |
6 | use rustc_errors::Applicability; | |
7 | use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath}; | |
8 | use rustc_lint::{LateContext, LateLintPass}; | |
9 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
10 | use rustc_span::sym; | |
11 | ||
12 | declare_clippy_lint! { | |
13 | /// **What it does:*** Checks for unnecessary `ok()` in if let. | |
14 | /// | |
15 | /// **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match | |
16 | /// on `Ok(pat)` | |
17 | /// | |
18 | /// **Known problems:** None. | |
19 | /// | |
20 | /// **Example:** | |
21 | /// ```ignore | |
22 | /// for i in iter { | |
23 | /// if let Some(value) = i.parse().ok() { | |
24 | /// vec.push(value) | |
25 | /// } | |
26 | /// } | |
27 | /// ``` | |
28 | /// Could be written: | |
29 | /// | |
30 | /// ```ignore | |
31 | /// for i in iter { | |
32 | /// if let Ok(value) = i.parse() { | |
33 | /// vec.push(value) | |
34 | /// } | |
35 | /// } | |
36 | /// ``` | |
37 | pub IF_LET_SOME_RESULT, | |
38 | style, | |
39 | "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead" | |
40 | } | |
41 | ||
42 | declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); | |
43 | ||
44 | impl<'tcx> LateLintPass<'tcx> for OkIfLet { | |
45 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
46 | if_chain! { //begin checking variables | |
cdc7bbd5 XL |
47 | if let ExprKind::Match(op, body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let |
48 | if let ExprKind::MethodCall(_, ok_span, result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _) | |
49 | if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = body[0].pat.kind; //get operation | |
f20569fa XL |
50 | if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; |
51 | if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); | |
52 | if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; | |
53 | ||
54 | then { | |
55 | let mut applicability = Applicability::MachineApplicable; | |
56 | let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); | |
57 | let trimmed_ok = snippet_with_applicability(cx, op.span.until(ok_span), "", &mut applicability); | |
58 | let sugg = format!( | |
59 | "if let Ok({}) = {}", | |
60 | some_expr_string, | |
61 | trimmed_ok.trim().trim_end_matches('.'), | |
62 | ); | |
63 | span_lint_and_sugg( | |
64 | cx, | |
65 | IF_LET_SOME_RESULT, | |
66 | expr.span.with_hi(op.span.hi()), | |
67 | "matching on `Some` with `ok()` is redundant", | |
68 | &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), | |
69 | sugg, | |
70 | applicability, | |
71 | ); | |
72 | } | |
73 | } | |
74 | } | |
75 | } |