about summary refs log tree commit diff
diff options
context:
space:
mode:
authory21 <30553356+y21@users.noreply.github.com>2023-10-09 21:39:16 +0200
committery21 <30553356+y21@users.noreply.github.com>2023-10-09 21:57:44 +0200
commitbba155ea9dac4f320303696572f552e80042889c (patch)
treed618749591b7fa3f434e2f0f8120039c936541b1
parent0b60531e42a9afcd063447bf0543d8441b0da3f6 (diff)
downloadrust-bba155ea9dac4f320303696572f552e80042889c.tar.gz
rust-bba155ea9dac4f320303696572f552e80042889c.zip
move changed logic to into its own util function
-rw-r--r--clippy_lints/src/methods/filter_map_identity.rs4
-rw-r--r--clippy_lints/src/methods/flat_map_identity.rs4
-rw-r--r--clippy_lints/src/methods/map_identity.rs4
-rw-r--r--clippy_utils/src/lib.rs71
4 files changed, 49 insertions, 34 deletions
diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs
index 3337b250c0e..20878f1e4df 100644
--- a/clippy_lints/src/methods/filter_map_identity.rs
+++ b/clippy_lints/src/methods/filter_map_identity.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_expr_identity_function, is_trait_method};
+use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -9,7 +9,7 @@ use rustc_span::sym;
 use super::FILTER_MAP_IDENTITY;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
-    if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
+    if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, filter_map_arg) {
         span_lint_and_sugg(
             cx,
             FILTER_MAP_IDENTITY,
diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs
index 84a21de0ac8..8849a4f4942 100644
--- a/clippy_lints/src/methods/flat_map_identity.rs
+++ b/clippy_lints/src/methods/flat_map_identity.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_expr_identity_function, is_trait_method};
+use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(
     flat_map_arg: &'tcx hir::Expr<'_>,
     flat_map_span: Span,
 ) {
-    if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
+    if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, flat_map_arg) {
         span_lint_and_sugg(
             cx,
             FLAT_MAP_IDENTITY,
diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs
index 7be1ce483f6..57581363cfa 100644
--- a/clippy_lints/src/methods/map_identity.rs
+++ b/clippy_lints/src/methods/map_identity.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expr_identity_function, is_trait_method};
+use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -23,7 +23,7 @@ pub(super) fn check(
         if is_trait_method(cx, expr, sym::Iterator)
             || is_type_diagnostic_item(cx, caller_ty, sym::Result)
             || is_type_diagnostic_item(cx, caller_ty, sym::Option);
-        if is_expr_identity_function(cx, map_arg);
+        if is_expr_untyped_identity_function(cx, map_arg);
         if let Some(sugg_span) = expr.span.trim_start(caller.span);
         then {
             span_lint_and_sugg(
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index e22bf11bb79..dc1b5a0a324 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -2027,36 +2027,31 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
 }
 
-/// Checks if an expression represents the identity function
-/// Only examines closures and `std::convert::identity`
+/// Checks if a function's body represents the identity function. Looks for bodies of the form:
+/// * `|x| x`
+/// * `|x| return x`
+/// * `|x| { return x }`
+/// * `|x| { return x; }`
 ///
-/// Closure bindings with type annotations and `std::convert::identity` with generic args
-/// are not considered identity functions because they can guide type inference,
-/// and removing it may lead to compile errors.
-pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
-    /// * `|x| x`
-    /// * `|x| return x`
-    /// * `|x| { return x }`
-    /// * `|x| { return x; }`
-    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
-        let id = if_chain! {
-            if let [param] = func.params;
-            if let PatKind::Binding(_, id, _, _) = param.pat.kind;
-            then {
-                id
-            } else {
-                return false;
-            }
-        };
+/// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead.
+fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
+    let id = if_chain! {
+        if let [param] = func.params;
+        if let PatKind::Binding(_, id, _, _) = param.pat.kind;
+        then {
+            id
+        } else {
+            return false;
+        }
+    };
 
-        let mut expr = func.value;
-        loop {
-            match expr.kind {
-                #[rustfmt::skip]
+    let mut expr = func.value;
+    loop {
+        match expr.kind {
+            #[rustfmt::skip]
                 ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
                 | ExprKind::Ret(Some(e)) => expr = e,
-                #[rustfmt::skip]
+            #[rustfmt::skip]
                 ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
                     if_chain! {
                         if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
@@ -2068,11 +2063,16 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
                         }
                     }
                 },
-                _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
-            }
+            _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
         }
     }
+}
 
+/// This is the same as [`is_expr_identity_function`], but does not consider closures
+/// with type annotations for its bindings (or similar) as identity functions:
+/// * `|x: u8| x`
+/// * `std::convert::identity::<u8>`
+pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match expr.kind {
         ExprKind::Closure(&Closure { body, fn_decl, .. })
             if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) =>
@@ -2089,6 +2089,21 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
     }
 }
 
+/// Checks if an expression represents the identity function
+/// Only examines closures and `std::convert::identity`
+///
+/// NOTE: If you want to use this function to find out if a closure is unnecessary, you likely want
+/// to call [`is_expr_untyped_identity_function`] instead, which makes sure that the closure doesn't
+/// have type annotations. This is important because removing a closure with bindings can
+/// remove type information that helped type inference before, which can then lead to compile
+/// errors.
+pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match expr.kind {
+        ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
+        _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
+    }
+}
+
 /// Gets the node where an expression is either used, or it's type is unified with another branch.
 /// Returns both the node and the `HirId` of the closest child node.
 pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {