about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/methods/mod.rs136
-rw-r--r--clippy_lints/src/methods/unnecessary_lazy_eval.rs113
2 files changed, 132 insertions, 117 deletions
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 70a6b1f5021..bfc89a74742 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -3,6 +3,7 @@ mod inefficient_to_string;
 mod manual_saturating_arithmetic;
 mod option_map_unwrap_or;
 mod unnecessary_filter_map;
+mod unnecessary_lazy_eval;
 
 use std::borrow::Cow;
 use std::fmt;
@@ -1436,17 +1437,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
             ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
             ["unwrap_or_else", "map"] => {
-                lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or");
-                lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]);
+                if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
+                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or");
+                }
             },
             ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
             ["and_then", ..] => {
-                lint_lazy_eval(cx, expr, arg_lists[0], false, "and");
+                unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and");
                 bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
                 bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
             },
             ["or_else", ..] => {
-                lint_lazy_eval(cx, expr, arg_lists[0], false, "or");
+                unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or");
                 bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]);
             },
             ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
@@ -1490,9 +1492,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
             ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
             ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
-            ["unwrap_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"),
-            ["get_or_insert_with", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "get_or_insert"),
-            ["ok_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "ok_or"),
+            ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"),
+            ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"),
+            ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"),
             _ => {},
         }
 
@@ -2708,119 +2710,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
     }
 }
 
-/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
-/// replaced with `<fn>(return value of simple closure)`
-fn lint_lazy_eval<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'_>,
-    args: &'tcx [hir::Expr<'_>],
-    allow_variant_calls: bool,
-    simplify_using: &str,
-) {
-    let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
-    let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));
-
-    if !is_option && !is_result {
-        return;
-    }
-
-    // Return true if the expression is an accessor of any of the arguments
-    fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool {
-        params.iter().any(|arg| {
-            if_chain! {
-                if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind;
-                if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind;
-                if let [p, ..] = path.segments;
-                then {
-                    ident.name == p.ident.name
-                } else {
-                    false
-                }
-            }
-        })
-    }
-
-    fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool {
-        paths.iter().any(|candidate| match_qpath(path, candidate))
-    }
-
-    fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool {
-        match expr.kind {
-            // Closures returning literals can be unconditionally simplified
-            hir::ExprKind::Lit(_) => true,
-
-            hir::ExprKind::Index(ref object, ref index) => {
-                // arguments are not being indexed into
-                if !expr_uses_argument(object, params) {
-                    // arguments are not used as index
-                    !expr_uses_argument(index, params)
-                } else {
-                    false
-                }
-            },
-
-            // Reading fields can be simplified if the object is not an argument of the closure
-            hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params),
-
-            // Paths can be simplified if the root is not the argument, this also covers None
-            hir::ExprKind::Path(_) => !expr_uses_argument(expr, params),
-
-            // Calls to Some, Ok, Err can be considered literals if they don't derive an argument
-            hir::ExprKind::Call(ref func, ref args) => if_chain! {
-                if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
-                if let hir::ExprKind::Path(ref path) = func.kind;
-                if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]);
-                then {
-                    // Recursively check all arguments
-                    args.iter().all(|arg| can_simplify(arg, params, variant_calls))
-                } else {
-                    false
-                }
-            },
-
-            // For anything more complex than the above, a closure is probably the right solution,
-            // or the case is handled by an other lint
-            _ => false,
-        }
-    }
-
-    if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
-        let body = cx.tcx.hir().body(eid);
-        let ex = &body.value;
-        let params = &body.params;
-
-        if can_simplify(ex, params, allow_variant_calls) {
-            let msg = if is_option {
-                "unnecessary closure used to substitute value for `Option::None`"
-            } else {
-                "unnecessary closure used to substitute value for `Result::Err`"
-            };
-
-            span_lint_and_sugg(
-                cx,
-                UNNECESSARY_LAZY_EVALUATION,
-                expr.span,
-                msg,
-                &format!("Use `{}` instead", simplify_using),
-                format!(
-                    "{0}.{1}({2})",
-                    snippet(cx, args[0].span, ".."),
-                    simplify_using,
-                    snippet(cx, ex.span, ".."),
-                ),
-                Applicability::MachineApplicable,
-            );
-        }
-    }
-}
-
 /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
 fn lint_map_unwrap_or_else<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'_>,
     map_args: &'tcx [hir::Expr<'_>],
     unwrap_args: &'tcx [hir::Expr<'_>],
-) {
+) -> bool {
     // lint if the caller of `map()` is an `Option`
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type));
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type));
@@ -2832,10 +2728,10 @@ fn lint_map_unwrap_or_else<'tcx>(
         let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx);
         if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
             if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
-                return;
+                return false;
             }
         } else {
-            return;
+            return false;
         }
 
         // lint message
@@ -2865,9 +2761,15 @@ fn lint_map_unwrap_or_else<'tcx>(
                     map_snippet, unwrap_snippet,
                 ),
             );
+            true
         } else if same_span && multiline {
             span_lint(cx, MAP_UNWRAP_OR, expr.span, msg);
-        };
+            true
+        } else {
+            false
+        }
+    } else {
+        false
     }
 }
 
diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
new file mode 100644
index 00000000000..0dbedc4919c
--- /dev/null
+++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -0,0 +1,113 @@
+use crate::utils::{match_qpath, span_lint_and_sugg, snippet, is_type_diagnostic_item};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+use super::UNNECESSARY_LAZY_EVALUATION;
+
+/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
+/// replaced with `<fn>(return value of simple closure)`
+pub(super) fn lint<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    args: &'tcx [hir::Expr<'_>],
+    allow_variant_calls: bool,
+    simplify_using: &str,
+) {
+    let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
+    let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));
+
+    if !is_option && !is_result {
+        return;
+    }
+
+    // Return true if the expression is an accessor of any of the arguments
+    fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool {
+        params.iter().any(|arg| {
+            if_chain! {
+                if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind;
+                if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind;
+                if let [p, ..] = path.segments;
+                then {
+                    ident.name == p.ident.name
+                } else {
+                    false
+                }
+            }
+        })
+    }
+
+    fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool {
+        paths.iter().any(|candidate| match_qpath(path, candidate))
+    }
+
+    fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool {
+        match expr.kind {
+            // Closures returning literals can be unconditionally simplified
+            hir::ExprKind::Lit(_) => true,
+
+            hir::ExprKind::Index(ref object, ref index) => {
+                // arguments are not being indexed into
+                if !expr_uses_argument(object, params) {
+                    // arguments are not used as index
+                    !expr_uses_argument(index, params)
+                } else {
+                    false
+                }
+            },
+
+            // Reading fields can be simplified if the object is not an argument of the closure
+            hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params),
+
+            // Paths can be simplified if the root is not the argument, this also covers None
+            hir::ExprKind::Path(_) => !expr_uses_argument(expr, params),
+
+            // Calls to Some, Ok, Err can be considered literals if they don't derive an argument
+            hir::ExprKind::Call(ref func, ref args) => if_chain! {
+                if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
+                if let hir::ExprKind::Path(ref path) = func.kind;
+                if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]);
+                then {
+                    // Recursively check all arguments
+                    args.iter().all(|arg| can_simplify(arg, params, variant_calls))
+                } else {
+                    false
+                }
+            },
+
+            // For anything more complex than the above, a closure is probably the right solution,
+            // or the case is handled by an other lint
+            _ => false,
+        }
+    }
+
+    if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
+        let body = cx.tcx.hir().body(eid);
+        let ex = &body.value;
+        let params = &body.params;
+
+        if can_simplify(ex, params, allow_variant_calls) {
+            let msg = if is_option {
+                "unnecessary closure used to substitute value for `Option::None`"
+            } else {
+                "unnecessary closure used to substitute value for `Result::Err`"
+            };
+
+            span_lint_and_sugg(
+                cx,
+                UNNECESSARY_LAZY_EVALUATION,
+                expr.span,
+                msg,
+                &format!("Use `{}` instead", simplify_using),
+                format!(
+                    "{0}.{1}({2})",
+                    snippet(cx, args[0].span, ".."),
+                    simplify_using,
+                    snippet(cx, ex.span, ".."),
+                ),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}