move changed logic to into its own util function

This commit is contained in:
y21 2023-10-09 21:39:16 +02:00
parent 0b60531e42
commit bba155ea9d
4 changed files with 49 additions and 34 deletions

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -9,7 +9,7 @@ use rustc_span::sym;
use super::FILTER_MAP_IDENTITY; use super::FILTER_MAP_IDENTITY;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { 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( span_lint_and_sugg(
cx, cx,
FILTER_MAP_IDENTITY, FILTER_MAP_IDENTITY,

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(
flat_map_arg: &'tcx hir::Expr<'_>, flat_map_arg: &'tcx hir::Expr<'_>,
flat_map_span: Span, 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( span_lint_and_sugg(
cx, cx,
FLAT_MAP_IDENTITY, FLAT_MAP_IDENTITY,

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item; 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_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -23,7 +23,7 @@ pub(super) fn check(
if is_trait_method(cx, expr, sym::Iterator) 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::Result)
|| is_type_diagnostic_item(cx, caller_ty, sym::Option); || 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); if let Some(sugg_span) = expr.span.trim_start(caller.span);
then { then {
span_lint_and_sugg( span_lint_and_sugg(

View File

@ -2027,18 +2027,13 @@ 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)) 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`
///
/// 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: /// Checks if a function's body represents the identity function. Looks for bodies of the form:
/// * `|x| x` /// * `|x| x`
/// * `|x| return x` /// * `|x| return x`
/// * `|x| { return x }` /// * `|x| { return x }`
/// * `|x| { return x; }` /// * `|x| { return x; }`
///
/// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead.
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
let id = if_chain! { let id = if_chain! {
if let [param] = func.params; if let [param] = func.params;
@ -2073,6 +2068,11 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
} }
} }
/// 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 { match expr.kind {
ExprKind::Closure(&Closure { body, fn_decl, .. }) ExprKind::Closure(&Closure { body, fn_decl, .. })
if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) => 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. /// 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. /// 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)> { pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {