]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
94222f64 | 2 | use clippy_utils::higher; |
cdc7bbd5 XL |
3 | use clippy_utils::method_chain_args; |
4 | use clippy_utils::source::snippet_with_applicability; | |
5 | use clippy_utils::ty::is_type_diagnostic_item; | |
f20569fa XL |
6 | use if_chain::if_chain; |
7 | use rustc_errors::Applicability; | |
94222f64 | 8 | use rustc_hir::{Expr, ExprKind, PatKind, QPath}; |
f20569fa XL |
9 | use rustc_lint::{LateContext, LateLintPass}; |
10 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
11 | use rustc_span::sym; | |
12 | ||
13 | declare_clippy_lint! { | |
94222f64 | 14 | /// ### What it does |
c295e0f8 | 15 | /// Checks for unnecessary `ok()` in `while let`. |
f20569fa | 16 | /// |
94222f64 | 17 | /// ### Why is this bad? |
c295e0f8 | 18 | /// Calling `ok()` in `while let` is unnecessary, instead match |
f20569fa XL |
19 | /// on `Ok(pat)` |
20 | /// | |
94222f64 | 21 | /// ### Example |
f20569fa | 22 | /// ```ignore |
c295e0f8 XL |
23 | /// while let Some(value) = iter.next().ok() { |
24 | /// vec.push(value) | |
f20569fa | 25 | /// } |
f20569fa | 26 | /// |
c295e0f8 XL |
27 | /// if let Some(valie) = iter.next().ok() { |
28 | /// vec.push(value) | |
29 | /// } | |
30 | /// ``` | |
31 | /// Use instead: | |
f20569fa | 32 | /// ```ignore |
c295e0f8 XL |
33 | /// while let Ok(value) = iter.next() { |
34 | /// vec.push(value) | |
35 | /// } | |
36 | /// | |
37 | /// if let Ok(value) = iter.next() { | |
3c0e092e | 38 | /// vec.push(value) |
f20569fa XL |
39 | /// } |
40 | /// ``` | |
a2a8927a | 41 | #[clippy::version = "1.57.0"] |
c295e0f8 | 42 | pub MATCH_RESULT_OK, |
f20569fa | 43 | style, |
c295e0f8 | 44 | "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead" |
f20569fa XL |
45 | } |
46 | ||
c295e0f8 | 47 | declare_lint_pass!(MatchResultOk => [MATCH_RESULT_OK]); |
f20569fa | 48 | |
c295e0f8 | 49 | impl<'tcx> LateLintPass<'tcx> for MatchResultOk { |
f20569fa | 50 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { |
c295e0f8 XL |
51 | let (let_pat, let_expr, ifwhile) = |
52 | if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) { | |
53 | (let_pat, let_expr, "if") | |
54 | } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { | |
55 | (let_pat, let_expr, "while") | |
56 | } else { | |
57 | return; | |
58 | }; | |
59 | ||
60 | if_chain! { | |
5099ac24 | 61 | if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _) |
c295e0f8 | 62 | if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation |
94222f64 | 63 | if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; |
c295e0f8 | 64 | if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result); |
f20569fa XL |
65 | if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; |
66 | ||
67 | then { | |
c295e0f8 | 68 | |
f20569fa XL |
69 | let mut applicability = Applicability::MachineApplicable; |
70 | let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); | |
5099ac24 | 71 | let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability); |
f20569fa | 72 | let sugg = format!( |
c295e0f8 XL |
73 | "{} let Ok({}) = {}", |
74 | ifwhile, | |
f20569fa XL |
75 | some_expr_string, |
76 | trimmed_ok.trim().trim_end_matches('.'), | |
77 | ); | |
78 | span_lint_and_sugg( | |
79 | cx, | |
c295e0f8 | 80 | MATCH_RESULT_OK, |
94222f64 | 81 | expr.span.with_hi(let_expr.span.hi()), |
f20569fa XL |
82 | "matching on `Some` with `ok()` is redundant", |
83 | &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), | |
84 | sugg, | |
85 | applicability, | |
86 | ); | |
87 | } | |
88 | } | |
89 | } | |
90 | } |