about summary refs log tree commit diff
diff options
context:
space:
mode:
authory21 <30553356+y21@users.noreply.github.com>2023-09-16 15:13:44 +0200
committery21 <30553356+y21@users.noreply.github.com>2023-09-16 15:13:44 +0200
commit2ec6f3b1ed22c3fa0b20eda5cf335c54f81b4462 (patch)
tree969bb945b380497b216f84130765f4f1698e211a
parent860e800fa08734ff9d15a67115f8220e5fa2202e (diff)
downloadrust-2ec6f3b1ed22c3fa0b20eda5cf335c54f81b4462.tar.gz
rust-2ec6f3b1ed22c3fa0b20eda5cf335c54f81b4462.zip
also count derefs through custom `Deref` impls
-rw-r--r--clippy_lints/src/methods/filter_map_bool_then.rs13
-rw-r--r--tests/ui/filter_map_bool_then.fixed11
-rw-r--r--tests/ui/filter_map_bool_then.rs11
-rw-r--r--tests/ui/filter_map_bool_then.stderr8
4 files changed, 36 insertions, 7 deletions
diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs
index dd4aa276663..33657254965 100644
--- a/clippy_lints/src/methods/filter_map_bool_then.rs
+++ b/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -2,12 +2,13 @@ use super::FILTER_MAP_BOOL_THEN;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::paths::BOOL_THEN;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{is_copy, peel_mid_ty_refs_is_mutable};
+use clippy_utils::ty::is_copy;
 use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::Binder;
 use rustc_span::{sym, Span};
 
@@ -36,11 +37,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
         && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
         && match_def_path(cx, def_id, &BOOL_THEN)
         && !is_from_proc_macro(cx, expr)
-        // Peel all refs (e.g. `&&&&mut &&&bool` -> `bool`) and get its count so we can suggest the exact
-        // amount of derefs to get to the bool in the filter.
-        // `peel_mid_ty_refs` alone doesn't handle mutable reference, so we use `_is_mutable`
-        // instead which counts them too and just ignore the resulting mutability
-        && let (_, needed_derefs, _) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv))
+        // Count the number of derefs needed to get to the bool because we need those in the suggestion
+        && let needed_derefs = cx.typeck_results().expr_adjustments(recv)
+            .iter()
+            .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
+            .count()
         && let Some(param_snippet) = snippet_opt(cx, param.span)
         && let Some(filter) = snippet_opt(cx, recv.span)
         && let Some(map) = snippet_opt(cx, then_body.span)
diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed
index d1c946585f5..6a1b81fdbcb 100644
--- a/tests/ui/filter_map_bool_then.fixed
+++ b/tests/ui/filter_map_bool_then.fixed
@@ -67,4 +67,15 @@ fn issue11503() {
     // Should also suggest derefs when going through a mutable reference
     let bools: &[&mut bool] = &[&mut true];
     let _: Vec<usize> = bools.iter().enumerate().filter(|&(i, b)| **b).map(|(i, b)| i).collect();
+
+    // Should also suggest derefs when going through a custom deref
+    struct DerefToBool;
+    impl std::ops::Deref for DerefToBool {
+        type Target = bool;
+        fn deref(&self) -> &Self::Target {
+            &true
+        }
+    }
+    let bools: &[&&DerefToBool] = &[&&DerefToBool];
+    let _: Vec<usize> = bools.iter().enumerate().filter(|&(i, b)| ****b).map(|(i, b)| i).collect();
 }
diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs
index 2d0ed282013..a41e88f8805 100644
--- a/tests/ui/filter_map_bool_then.rs
+++ b/tests/ui/filter_map_bool_then.rs
@@ -67,4 +67,15 @@ fn issue11503() {
     // Should also suggest derefs when going through a mutable reference
     let bools: &[&mut bool] = &[&mut true];
     let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect();
+
+    // Should also suggest derefs when going through a custom deref
+    struct DerefToBool;
+    impl std::ops::Deref for DerefToBool {
+        type Target = bool;
+        fn deref(&self) -> &Self::Target {
+            &true
+        }
+    }
+    let bools: &[&&DerefToBool] = &[&&DerefToBool];
+    let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect();
 }
diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr
index 174786ff32e..fab6987913a 100644
--- a/tests/ui/filter_map_bool_then.stderr
+++ b/tests/ui/filter_map_bool_then.stderr
@@ -55,5 +55,11 @@ error: usage of `bool::then` in `filter_map`
 LL |     let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect();
    |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| **b).map(|(i, b)| i)`
 
-error: aborting due to 9 previous errors
+error: usage of `bool::then` in `filter_map`
+  --> $DIR/filter_map_bool_then.rs:80:50
+   |
+LL |     let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect();
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ****b).map(|(i, b)| i)`
+
+error: aborting due to 10 previous errors