about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-08-16 20:27:43 +0000
committerbors <bors@rust-lang.org>2020-08-16 20:27:43 +0000
commit8d0d89adc81be1fa7766ff17aa4f2c6d5e8c69f7 (patch)
tree643ec7d5623db9c133c8019fbfb2747a6f5d6d47
parent5d723d0de18441e72264335dea3e80dd9fee970e (diff)
parentfc1e07e0c1803edb3ade2db2f46034cf227642c9 (diff)
downloadrust-8d0d89adc81be1fa7766ff17aa4f2c6d5e8c69f7.tar.gz
rust-8d0d89adc81be1fa7766ff17aa4f2c6d5e8c69f7.zip
Auto merge of #5720 - bugadani:new-lint, r=flip1995,yaahc
Add unnecessary lazy evaluation lint

changelog: Add [`unnecessary_lazy_evaluations`] lint that checks for usages of `unwrap_or_else` and similar functions that can be simplified.

Closes #5715
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/lib.rs3
-rw-r--r--clippy_lints/src/methods/mod.rs62
-rw-r--r--clippy_lints/src/methods/unnecessary_lazy_eval.rs111
-rw-r--r--src/lintlist/mod.rs7
-rw-r--r--tests/ui/unnecessary_lazy_eval.fixed117
-rw-r--r--tests/ui/unnecessary_lazy_eval.rs117
-rw-r--r--tests/ui/unnecessary_lazy_eval.stderr148
8 files changed, 561 insertions, 5 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 50fe7612909..f662de122f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1754,6 +1754,7 @@ Released 2018-09-13
 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
+[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 986e9d9bee4..17501e8e6da 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -685,6 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &methods::UNINIT_ASSUMED_INIT,
         &methods::UNNECESSARY_FILTER_MAP,
         &methods::UNNECESSARY_FOLD,
+        &methods::UNNECESSARY_LAZY_EVALUATIONS,
         &methods::UNWRAP_USED,
         &methods::USELESS_ASREF,
         &methods::WRONG_PUB_SELF_CONVENTION,
@@ -1360,6 +1361,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&methods::UNINIT_ASSUMED_INIT),
         LintId::of(&methods::UNNECESSARY_FILTER_MAP),
         LintId::of(&methods::UNNECESSARY_FOLD),
+        LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS),
         LintId::of(&methods::USELESS_ASREF),
         LintId::of(&methods::WRONG_SELF_CONVENTION),
         LintId::of(&methods::ZST_OFFSET),
@@ -1540,6 +1542,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
         LintId::of(&methods::STRING_EXTEND_CHARS),
         LintId::of(&methods::UNNECESSARY_FOLD),
+        LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS),
         LintId::of(&methods::WRONG_SELF_CONVENTION),
         LintId::of(&misc::TOPLEVEL_REF_ARG),
         LintId::of(&misc::ZERO_PTR),
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 2330978e67f..0f50a4c813a 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;
@@ -1329,6 +1330,42 @@ declare_clippy_lint! {
     "`push_str()` used with a single-character string literal as parameter"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary
+    /// lazily evaluated closures on `Option` and `Result`.
+    ///
+    /// This lint suggests changing the following functions, when eager evaluation results in
+    /// simpler code:
+    ///  - `unwrap_or_else` to `unwrap_or`
+    ///  - `and_then` to `and`
+    ///  - `or_else` to `or`
+    ///  - `get_or_insert_with` to `get_or_insert`
+    ///  - `ok_or_else` to `ok_or`
+    ///
+    /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.
+    ///
+    /// **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have
+    /// side effects. Eagerly evaluating them can change the semantics of the program.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// // example code where clippy issues a warning
+    /// let opt: Option<u32> = None;
+    ///
+    /// opt.unwrap_or_else(|| 42);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let opt: Option<u32> = None;
+    ///
+    /// opt.unwrap_or(42);
+    /// ```
+    pub UNNECESSARY_LAZY_EVALUATIONS,
+    style,
+    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
+}
+
 declare_lint_pass!(Methods => [
     UNWRAP_USED,
     EXPECT_USED,
@@ -1378,6 +1415,7 @@ declare_lint_pass!(Methods => [
     ZST_OFFSET,
     FILETYPE_IS_FILE,
     OPTION_AS_REF_DEREF,
+    UNNECESSARY_LAZY_EVALUATIONS,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Methods {
@@ -1398,13 +1436,19 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
             ["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_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
+            ["unwrap_or_else", "map"] => {
+                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", ..] => {
+                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", ..] => {
+                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]),
@@ -1448,6 +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", ..] => 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"),
             _ => {},
         }
 
@@ -2664,12 +2711,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
 }
 
 /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
+/// Return true if lint triggered
 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));
@@ -2681,10 +2729,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
@@ -2714,10 +2762,14 @@ fn lint_map_unwrap_or_else<'tcx>(
                     map_snippet, unwrap_snippet,
                 ),
             );
+            return true;
         } else if same_span && multiline {
             span_lint(cx, MAP_UNWRAP_OR, expr.span, msg);
-        };
+            return true;
+        }
     }
+
+    false
 }
 
 /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
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..31517659c34
--- /dev/null
+++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -0,0 +1,111 @@
+use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+use super::UNNECESSARY_LAZY_EVALUATIONS;
+
+// 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) {
+                false
+            } else {
+                // arguments are not used as index
+                !expr_uses_argument(index, params)
+            }
+        },
+
+        // 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,
+    }
+}
+
+/// 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 {
+        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_EVALUATIONS,
+                    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,
+                );
+            }
+        }
+    }
+}
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index 233f95deedd..3229c8da507 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -2384,6 +2384,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         module: "methods",
     },
     Lint {
+        name: "unnecessary_lazy_evaluations",
+        group: "style",
+        desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation",
+        deprecation: None,
+        module: "methods",
+    },
+    Lint {
         name: "unnecessary_mut_passed",
         group: "style",
         desc: "an argument passed as a mutable reference although the callee only demands an immutable reference",
diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed
new file mode 100644
index 00000000000..fa66e68794e
--- /dev/null
+++ b/tests/ui/unnecessary_lazy_eval.fixed
@@ -0,0 +1,117 @@
+// run-rustfix
+#![warn(clippy::unnecessary_lazy_evaluations)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::bind_instead_of_map)]
+
+struct Deep(Option<usize>);
+
+#[derive(Copy, Clone)]
+struct SomeStruct {
+    some_field: usize,
+}
+
+impl SomeStruct {
+    fn return_some_field(&self) -> usize {
+        self.some_field
+    }
+}
+
+fn some_call<T: Default>() -> T {
+    T::default()
+}
+
+fn main() {
+    let astronomers_pi = 10;
+    let ext_arr: [usize; 1] = [2];
+    let ext_str = SomeStruct { some_field: 10 };
+
+    let mut opt = Some(42);
+    let ext_opt = Some(42);
+    let nested_opt = Some(Some(42));
+    let nested_tuple_opt = Some(Some((42, 43)));
+
+    // Should lint - Option
+    let _ = opt.unwrap_or(2);
+    let _ = opt.unwrap_or(astronomers_pi);
+    let _ = opt.unwrap_or(ext_str.some_field);
+    let _ = opt.unwrap_or(ext_arr[0]);
+    let _ = opt.and(ext_opt);
+    let _ = opt.or(ext_opt);
+    let _ = opt.or(None);
+    let _ = opt.get_or_insert(2);
+    let _ = opt.ok_or(2);
+    let _ = opt.ok_or(ext_arr[0]);
+
+    // Cases when unwrap is not called on a simple variable
+    let _ = Some(10).unwrap_or(2);
+    let _ = Some(10).and(ext_opt);
+    let _: Option<usize> = None.or(ext_opt);
+    let _ = None.get_or_insert(2);
+    let _: Result<usize, usize> = None.ok_or(2);
+    let _: Option<usize> = None.or(None);
+
+    let mut deep = Deep(Some(42));
+    let _ = deep.0.unwrap_or(2);
+    let _ = deep.0.and(ext_opt);
+    let _ = deep.0.or(None);
+    let _ = deep.0.get_or_insert(2);
+    let _ = deep.0.ok_or(2);
+
+    // Should not lint - Option
+    let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
+    let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
+    let _ = opt.or_else(some_call);
+    let _ = opt.or_else(|| some_call());
+    let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
+    let _: Result<usize, usize> = opt.ok_or_else(some_call);
+    let _ = deep.0.get_or_insert_with(|| some_call());
+    let _ = deep.0.or_else(some_call);
+    let _ = deep.0.or_else(|| some_call());
+
+    // These are handled by bind_instead_of_map
+    let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
+    let _ = Some(10).and_then(|idx| Some(idx));
+    let _: Option<usize> = None.or_else(|| Some(3));
+    let _ = deep.0.or_else(|| Some(3));
+    let _ = opt.or_else(|| Some(3));
+
+    // Should lint - Result
+    let res: Result<usize, usize> = Err(5);
+    let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
+
+    let _ = res2.unwrap_or(2);
+    let _ = res2.unwrap_or(astronomers_pi);
+    let _ = res2.unwrap_or(ext_str.some_field);
+
+    // Should not lint - Result
+    let _ = res.unwrap_or_else(|err| err);
+    let _ = res.unwrap_or_else(|err| ext_arr[err]);
+    let _ = res2.unwrap_or_else(|err| err.some_field);
+    let _ = res2.unwrap_or_else(|err| err.return_some_field());
+    let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
+
+    let _: Result<usize, usize> = res.and_then(|x| Ok(x));
+    let _: Result<usize, usize> = res.and_then(|x| Err(x));
+
+    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
+    let _: Result<usize, usize> = res.or_else(|err| Err(err));
+
+    // These are handled by bind_instead_of_map
+    let _: Result<usize, usize> = res.and_then(|_| Ok(2));
+    let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.and_then(|_| Err(2));
+    let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or_else(|_| Ok(2));
+    let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or_else(|_| Err(2));
+    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+}
diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs
new file mode 100644
index 00000000000..04f47d1aa29
--- /dev/null
+++ b/tests/ui/unnecessary_lazy_eval.rs
@@ -0,0 +1,117 @@
+// run-rustfix
+#![warn(clippy::unnecessary_lazy_evaluations)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::bind_instead_of_map)]
+
+struct Deep(Option<usize>);
+
+#[derive(Copy, Clone)]
+struct SomeStruct {
+    some_field: usize,
+}
+
+impl SomeStruct {
+    fn return_some_field(&self) -> usize {
+        self.some_field
+    }
+}
+
+fn some_call<T: Default>() -> T {
+    T::default()
+}
+
+fn main() {
+    let astronomers_pi = 10;
+    let ext_arr: [usize; 1] = [2];
+    let ext_str = SomeStruct { some_field: 10 };
+
+    let mut opt = Some(42);
+    let ext_opt = Some(42);
+    let nested_opt = Some(Some(42));
+    let nested_tuple_opt = Some(Some((42, 43)));
+
+    // Should lint - Option
+    let _ = opt.unwrap_or_else(|| 2);
+    let _ = opt.unwrap_or_else(|| astronomers_pi);
+    let _ = opt.unwrap_or_else(|| ext_str.some_field);
+    let _ = opt.unwrap_or_else(|| ext_arr[0]);
+    let _ = opt.and_then(|_| ext_opt);
+    let _ = opt.or_else(|| ext_opt);
+    let _ = opt.or_else(|| None);
+    let _ = opt.get_or_insert_with(|| 2);
+    let _ = opt.ok_or_else(|| 2);
+    let _ = opt.ok_or_else(|| ext_arr[0]);
+
+    // Cases when unwrap is not called on a simple variable
+    let _ = Some(10).unwrap_or_else(|| 2);
+    let _ = Some(10).and_then(|_| ext_opt);
+    let _: Option<usize> = None.or_else(|| ext_opt);
+    let _ = None.get_or_insert_with(|| 2);
+    let _: Result<usize, usize> = None.ok_or_else(|| 2);
+    let _: Option<usize> = None.or_else(|| None);
+
+    let mut deep = Deep(Some(42));
+    let _ = deep.0.unwrap_or_else(|| 2);
+    let _ = deep.0.and_then(|_| ext_opt);
+    let _ = deep.0.or_else(|| None);
+    let _ = deep.0.get_or_insert_with(|| 2);
+    let _ = deep.0.ok_or_else(|| 2);
+
+    // Should not lint - Option
+    let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
+    let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
+    let _ = opt.or_else(some_call);
+    let _ = opt.or_else(|| some_call());
+    let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
+    let _: Result<usize, usize> = opt.ok_or_else(some_call);
+    let _ = deep.0.get_or_insert_with(|| some_call());
+    let _ = deep.0.or_else(some_call);
+    let _ = deep.0.or_else(|| some_call());
+
+    // These are handled by bind_instead_of_map
+    let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
+    let _ = Some(10).and_then(|idx| Some(idx));
+    let _: Option<usize> = None.or_else(|| Some(3));
+    let _ = deep.0.or_else(|| Some(3));
+    let _ = opt.or_else(|| Some(3));
+
+    // Should lint - Result
+    let res: Result<usize, usize> = Err(5);
+    let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
+
+    let _ = res2.unwrap_or_else(|_| 2);
+    let _ = res2.unwrap_or_else(|_| astronomers_pi);
+    let _ = res2.unwrap_or_else(|_| ext_str.some_field);
+
+    // Should not lint - Result
+    let _ = res.unwrap_or_else(|err| err);
+    let _ = res.unwrap_or_else(|err| ext_arr[err]);
+    let _ = res2.unwrap_or_else(|err| err.some_field);
+    let _ = res2.unwrap_or_else(|err| err.return_some_field());
+    let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
+
+    let _: Result<usize, usize> = res.and_then(|x| Ok(x));
+    let _: Result<usize, usize> = res.and_then(|x| Err(x));
+
+    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
+    let _: Result<usize, usize> = res.or_else(|err| Err(err));
+
+    // These are handled by bind_instead_of_map
+    let _: Result<usize, usize> = res.and_then(|_| Ok(2));
+    let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.and_then(|_| Err(2));
+    let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or_else(|_| Ok(2));
+    let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or_else(|_| Err(2));
+    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+}
diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr
new file mode 100644
index 00000000000..5c1b2eb1f14
--- /dev/null
+++ b/tests/ui/unnecessary_lazy_eval.stderr
@@ -0,0 +1,148 @@
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:34:13
+   |
+LL |     let _ = opt.unwrap_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)`
+   |
+   = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:35:13
+   |
+LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:36:13
+   |
+LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:37:13
+   |
+LL |     let _ = opt.unwrap_or_else(|| ext_arr[0]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:38:13
+   |
+LL |     let _ = opt.and_then(|_| ext_opt);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:39:13
+   |
+LL |     let _ = opt.or_else(|| ext_opt);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:40:13
+   |
+LL |     let _ = opt.or_else(|| None);
+   |             ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:41:13
+   |
+LL |     let _ = opt.get_or_insert_with(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:42:13
+   |
+LL |     let _ = opt.ok_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:43:13
+   |
+LL |     let _ = opt.ok_or_else(|| ext_arr[0]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:46:13
+   |
+LL |     let _ = Some(10).unwrap_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:47:13
+   |
+LL |     let _ = Some(10).and_then(|_| ext_opt);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:48:28
+   |
+LL |     let _: Option<usize> = None.or_else(|| ext_opt);
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:49:13
+   |
+LL |     let _ = None.get_or_insert_with(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:50:35
+   |
+LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
+   |                                   ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:51:28
+   |
+LL |     let _: Option<usize> = None.or_else(|| None);
+   |                            ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:54:13
+   |
+LL |     let _ = deep.0.unwrap_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:55:13
+   |
+LL |     let _ = deep.0.and_then(|_| ext_opt);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:56:13
+   |
+LL |     let _ = deep.0.or_else(|| None);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:57:13
+   |
+LL |     let _ = deep.0.get_or_insert_with(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:58:13
+   |
+LL |     let _ = deep.0.ok_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:84:13
+   |
+LL |     let _ = res2.unwrap_or_else(|_| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:85:13
+   |
+LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:86:13
+   |
+LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)`
+
+error: aborting due to 24 previous errors
+