about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/lib.register_all.rs2
-rw-r--r--clippy_lints/src/lib.register_complexity.rs2
-rw-r--r--clippy_lints/src/lib.register_lints.rs2
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/methods/mod.rs29
-rw-r--r--clippy_lints/src/methods/needless_option_as_deref.rs37
-rw-r--r--clippy_lints/src/needless_option_as_deref.rs65
-rw-r--r--tests/ui/needless_option_as_deref.fixed30
-rw-r--r--tests/ui/needless_option_as_deref.rs30
-rw-r--r--tests/ui/needless_option_as_deref.stderr12
10 files changed, 136 insertions, 75 deletions
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 1fb3ca1fd9b..341c7e397d3 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -176,6 +176,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
     LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::NEW_RET_NO_SELF),
     LintId::of(methods::OK_EXPECT),
@@ -225,7 +226,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(needless_bool::NEEDLESS_BOOL),
     LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
     LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
-    LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
     LintId::of(needless_update::NEEDLESS_UPDATE),
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs
index a2ce69065f9..10369a855ae 100644
--- a/clippy_lints/src/lib.register_complexity.rs
+++ b/clippy_lints/src/lib.register_complexity.rs
@@ -44,6 +44,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(methods::MANUAL_SPLIT_ONCE),
     LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(methods::OPTION_FILTER_MAP),
@@ -60,7 +61,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(needless_bool::BOOL_COMPARISON),
     LintId::of(needless_bool::NEEDLESS_BOOL),
     LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
-    LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
     LintId::of(needless_update::NEEDLESS_UPDATE),
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index bba3cae45f5..e5f5c6cd31c 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -315,6 +315,7 @@ store.register_lints(&[
     methods::MAP_FLATTEN,
     methods::MAP_IDENTITY,
     methods::MAP_UNWRAP_OR,
+    methods::NEEDLESS_OPTION_AS_DEREF,
     methods::NEEDLESS_SPLITN,
     methods::NEW_RET_NO_SELF,
     methods::OK_EXPECT,
@@ -386,7 +387,6 @@ store.register_lints(&[
     needless_continue::NEEDLESS_CONTINUE,
     needless_for_each::NEEDLESS_FOR_EACH,
     needless_late_init::NEEDLESS_LATE_INIT,
-    needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
     needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
     needless_question_mark::NEEDLESS_QUESTION_MARK,
     needless_update::NEEDLESS_UPDATE,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 8dab039f24f..a9a77f5ae04 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -308,7 +308,6 @@ mod needless_borrowed_ref;
 mod needless_continue;
 mod needless_for_each;
 mod needless_late_init;
-mod needless_option_as_deref;
 mod needless_pass_by_value;
 mod needless_question_mark;
 mod needless_update;
@@ -536,7 +535,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(ptr::Ptr));
     store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
     store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
-    store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref));
     store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
     store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
     store.register_late_pass(|| Box::new(misc::MiscLints));
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index c5c871c9d29..4a2bcca13b4 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -41,6 +41,7 @@ mod map_collect_result_unit;
 mod map_flatten;
 mod map_identity;
 mod map_unwrap_or;
+mod needless_option_as_deref;
 mod ok_expect;
 mod option_as_ref_deref;
 mod option_map_or_none;
@@ -2106,6 +2107,30 @@ declare_clippy_lint! {
     "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
+    /// for example, `Option<&T>::as_deref()` returns the same type.
+    ///
+    /// ### Why is this bad?
+    /// Redundant code and improving readability.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a = Some(&1);
+    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
+    /// ```
+    /// Could be written as:
+    /// ```rust
+    /// let a = Some(&1);
+    /// let b = a;
+    /// ```
+    #[clippy::version = "1.57.0"]
+    pub NEEDLESS_OPTION_AS_DEREF,
+    complexity,
+    "no-op use of `deref` or `deref_mut` method to `Option`."
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -2193,6 +2218,7 @@ impl_lint_pass!(Methods => [
     UNNECESSARY_TO_OWNED,
     UNNECESSARY_JOIN,
     ERR_EXPECT,
+    NEEDLESS_OPTION_AS_DEREF,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2425,6 +2451,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                     unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
                 }
             },
+            ("as_deref" | "as_deref_mut", []) => {
+                needless_option_as_deref::check(cx, expr, recv, name);
+            },
             ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
             ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs
new file mode 100644
index 00000000000..7030baf19ff
--- /dev/null
+++ b/clippy_lints/src/methods/needless_option_as_deref.rs
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::path_res;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::local_used_after_expr;
+use rustc_errors::Applicability;
+use rustc_hir::def::Res;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::NEEDLESS_OPTION_AS_DEREF;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) {
+    let typeck = cx.typeck_results();
+    let outer_ty = typeck.expr_ty(expr);
+
+    if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) {
+        if name == "as_deref_mut" && recv.is_syntactic_place_expr() {
+            let Res::Local(binding_id) = path_res(cx, recv) else { return };
+
+            if local_used_after_expr(cx, binding_id, recv) {
+                return;
+            }
+        }
+
+        span_lint_and_sugg(
+            cx,
+            NEEDLESS_OPTION_AS_DEREF,
+            expr.span,
+            "derefed type is same as origin",
+            "try this",
+            snippet_opt(cx, recv.span).unwrap(),
+            Applicability::MachineApplicable,
+        );
+    }
+}
diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs
deleted file mode 100644
index 9d3d7d1f24c..00000000000
--- a/clippy_lints/src/needless_option_as_deref.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::is_type_diagnostic_item;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for no-op uses of Option::{as_deref,as_deref_mut},
-    /// for example, `Option<&T>::as_deref()` returns the same type.
-    ///
-    /// ### Why is this bad?
-    /// Redundant code and improving readability.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let a = Some(&1);
-    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
-    /// ```
-    /// Could be written as:
-    /// ```rust
-    /// let a = Some(&1);
-    /// let b = a;
-    /// ```
-    #[clippy::version = "1.57.0"]
-    pub NEEDLESS_OPTION_AS_DEREF,
-    complexity,
-    "no-op use of `deref` or `deref_mut` method to `Option`."
-}
-
-declare_lint_pass!(OptionNeedlessDeref=> [
-    NEEDLESS_OPTION_AS_DEREF,
-]);
-
-impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-        let typeck = cx.typeck_results();
-        let outer_ty = typeck.expr_ty(expr);
-
-        if_chain! {
-            if is_type_diagnostic_item(cx,outer_ty,sym::Option);
-            if let ExprKind::MethodCall(path, [sub_expr], _) = expr.kind;
-            let symbol = path.ident.as_str();
-            if symbol == "as_deref" || symbol == "as_deref_mut";
-            if outer_ty == typeck.expr_ty(sub_expr);
-            then{
-                span_lint_and_sugg(
-                    cx,
-                    NEEDLESS_OPTION_AS_DEREF,
-                    expr.span,
-                    "derefed type is same as origin",
-                    "try this",
-                    snippet_opt(cx,sub_expr.span).unwrap(),
-                    Applicability::MachineApplicable
-                );
-            }
-        }
-    }
-}
diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed
index d721452ae88..c09b07db3dc 100644
--- a/tests/ui/needless_option_as_deref.fixed
+++ b/tests/ui/needless_option_as_deref.fixed
@@ -1,13 +1,41 @@
 // run-rustfix
 
-#[warn(clippy::needless_option_as_deref)]
+#![allow(unused)]
+#![warn(clippy::needless_option_as_deref)]
 
 fn main() {
     // should lint
     let _: Option<&usize> = Some(&1);
     let _: Option<&mut usize> = Some(&mut 1);
 
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    let _ = x;
+
     // should not lint
     let _ = Some(Box::new(1)).as_deref();
     let _ = Some(Box::new(1)).as_deref_mut();
+
+    // #7846
+    let mut i = 0;
+    let mut opt_vec = vec![Some(&mut i)];
+    opt_vec[0].as_deref_mut().unwrap();
+
+    let mut i = 0;
+    let x = &mut Some(&mut i);
+    (*x).as_deref_mut();
+
+    // #8047
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    x.as_deref_mut();
+    dbg!(x);
+}
+
+struct S<'a> {
+    opt: Option<&'a mut usize>,
+}
+
+fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
+    s.opt.as_deref_mut()
 }
diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs
index bb15512adf6..c3ba27ecccf 100644
--- a/tests/ui/needless_option_as_deref.rs
+++ b/tests/ui/needless_option_as_deref.rs
@@ -1,13 +1,41 @@
 // run-rustfix
 
-#[warn(clippy::needless_option_as_deref)]
+#![allow(unused)]
+#![warn(clippy::needless_option_as_deref)]
 
 fn main() {
     // should lint
     let _: Option<&usize> = Some(&1).as_deref();
     let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
 
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    let _ = x.as_deref_mut();
+
     // should not lint
     let _ = Some(Box::new(1)).as_deref();
     let _ = Some(Box::new(1)).as_deref_mut();
+
+    // #7846
+    let mut i = 0;
+    let mut opt_vec = vec![Some(&mut i)];
+    opt_vec[0].as_deref_mut().unwrap();
+
+    let mut i = 0;
+    let x = &mut Some(&mut i);
+    (*x).as_deref_mut();
+
+    // #8047
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    x.as_deref_mut();
+    dbg!(x);
+}
+
+struct S<'a> {
+    opt: Option<&'a mut usize>,
+}
+
+fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
+    s.opt.as_deref_mut()
 }
diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr
index 5dd507b4a71..bc07db5b38e 100644
--- a/tests/ui/needless_option_as_deref.stderr
+++ b/tests/ui/needless_option_as_deref.stderr
@@ -1,5 +1,5 @@
 error: derefed type is same as origin
-  --> $DIR/needless_option_as_deref.rs:7:29
+  --> $DIR/needless_option_as_deref.rs:8:29
    |
 LL |     let _: Option<&usize> = Some(&1).as_deref();
    |                             ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)`
@@ -7,10 +7,16 @@ LL |     let _: Option<&usize> = Some(&1).as_deref();
    = note: `-D clippy::needless-option-as-deref` implied by `-D warnings`
 
 error: derefed type is same as origin
-  --> $DIR/needless_option_as_deref.rs:8:33
+  --> $DIR/needless_option_as_deref.rs:9:33
    |
 LL |     let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)`
 
-error: aborting due to 2 previous errors
+error: derefed type is same as origin
+  --> $DIR/needless_option_as_deref.rs:13:13
+   |
+LL |     let _ = x.as_deref_mut();
+   |             ^^^^^^^^^^^^^^^^ help: try this: `x`
+
+error: aborting due to 3 previous errors