|
| 1 | +use crate::utils::paths; |
| 2 | +use crate::utils::{in_macro_or_desugar, match_qpath, match_type, remove_blocks, snippet, span_lint_and_sugg}; |
| 3 | +use rustc::hir; |
| 4 | +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; |
| 5 | +use rustc::{declare_lint_pass, declare_tool_lint}; |
| 6 | +use rustc_errors::Applicability; |
| 7 | + |
| 8 | +declare_clippy_lint! { |
| 9 | + /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`. |
| 10 | + /// |
| 11 | + /// **Why is this bad?** Readability, this can be written more concisely as |
| 12 | + /// `_.map(|x| y)`. |
| 13 | + /// |
| 14 | + /// **Known problems:** None |
| 15 | + /// |
| 16 | + /// **Example:** |
| 17 | + /// |
| 18 | + /// ```rust |
| 19 | + /// let x = Some("foo"); |
| 20 | + /// x.and_then(|s| Some(s.len())); |
| 21 | + /// ``` |
| 22 | + /// |
| 23 | + /// The correct use would be: |
| 24 | + /// |
| 25 | + /// ```rust |
| 26 | + /// let x = Some("foo"); |
| 27 | + /// x.map(|s| s.len()); |
| 28 | + /// ``` |
| 29 | + pub OPTION_AND_THEN_SOME, |
| 30 | + complexity, |
| 31 | + "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" |
| 32 | +} |
| 33 | + |
| 34 | +declare_lint_pass!(AndThenSomeLint => [OPTION_AND_THEN_SOME]); |
| 35 | + |
| 36 | +const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"; |
| 37 | + |
| 38 | +impl LateLintPass<'_, '_> for AndThenSomeLint { |
| 39 | + /// Lint use of `_.and_then(|x| Some(y))` for `Option`s |
| 40 | + fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) { |
| 41 | + if in_macro_or_desugar(e.span) { |
| 42 | + return; |
| 43 | + } |
| 44 | + |
| 45 | + if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.node { |
| 46 | + if args.len() == 2 && method.ident.as_str() == "and_then" { |
| 47 | + let ty = cx.tables.expr_ty(&args[0]); |
| 48 | + if !match_type(cx, ty, &paths::OPTION) { |
| 49 | + return; |
| 50 | + } |
| 51 | + match args[1].node { |
| 52 | + hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { |
| 53 | + let closure_body = cx.tcx.hir().body(body_id); |
| 54 | + let closure_expr = remove_blocks(&closure_body.value); |
| 55 | + |
| 56 | + // let note = format!("{:?}", args[1]); |
| 57 | + // span_note_and_lint(cx, OPTION_AND_THEN_SOME, closure_args_span, "Outer debugging |
| 58 | + // information", closure_args_span, ¬e); |
| 59 | + |
| 60 | + if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.node { |
| 61 | + if let hir::ExprKind::Path(ref qpath) = some_expr.node { |
| 62 | + if match_qpath(qpath, &paths::OPTION_SOME) { |
| 63 | + if some_args.len() == 1 { |
| 64 | + let inner_expr = &some_args[0]; |
| 65 | + let some_inner_snip = snippet(cx, inner_expr.span, ".."); |
| 66 | + let closure_args_snip = snippet(cx, closure_args_span, ".."); |
| 67 | + let option_snip = snippet(cx, args[0].span, ".."); |
| 68 | + let note = |
| 69 | + format!("{}.map({} {})", option_snip, closure_args_snip, some_inner_snip); |
| 70 | + span_lint_and_sugg( |
| 71 | + cx, |
| 72 | + OPTION_AND_THEN_SOME, |
| 73 | + e.span, |
| 74 | + LINT_MSG, |
| 75 | + "try this", |
| 76 | + note, |
| 77 | + Applicability::MachineApplicable, |
| 78 | + ); |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + }, |
| 84 | + // hir::ExprKind::Path(ref qpath) => { |
| 85 | + // if match_qpath(qpath, &paths::OPTION_SOME) { |
| 86 | + // let note = format!("{:?}", args[1]); |
| 87 | + // span_note_and_lint(cx, OPTION_AND_THEN_SOME, args[1].span, "Some debugging information", |
| 88 | + // args[1].span, ¬e); } |
| 89 | + // }, |
| 90 | + _ => {}, |
| 91 | + } |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | +} |
0 commit comments