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