| use clippy_utils::diagnostics::span_lint_and_then; |
| use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; |
| use clippy_utils::source::snippet_with_applicability; |
| use clippy_utils::ty::is_copy; |
| use clippy_utils::{is_expr_untyped_identity_function, is_mutable, path_to_local_with_projections}; |
| use rustc_errors::Applicability; |
| use rustc_hir::{self as hir, ExprKind, Node, PatKind}; |
| use rustc_lint::{LateContext, LintContext}; |
| use rustc_span::{Span, Symbol, sym}; |
| |
| use super::MAP_IDENTITY; |
| |
| const MSG: &str = "unnecessary map of the identity function"; |
| |
| pub(super) fn check( |
| cx: &LateContext<'_>, |
| expr: &hir::Expr<'_>, |
| caller: &hir::Expr<'_>, |
| map_arg: &hir::Expr<'_>, |
| name: Symbol, |
| _map_span: Span, |
| ) { |
| let caller_ty = cx.typeck_results().expr_ty(caller); |
| |
| if (cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) |
| || caller_ty.is_diag_item(cx, sym::Result) |
| || caller_ty.is_diag_item(cx, sym::Option)) |
| && is_expr_untyped_identity_function(cx, map_arg) |
| && let Some(call_span) = expr.span.trim_start(caller.span) |
| { |
| span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { |
| let main_sugg = (call_span, String::new()); |
| let mut app = if is_copy(cx, caller_ty) { |
| // there is technically a behavioral change here for `Copy` iterators, where |
| // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and |
| // changing it to `iter.next()` mutates iter directly |
| Applicability::Unspecified |
| } else { |
| Applicability::MachineApplicable |
| }; |
| |
| let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); |
| if needs_to_be_mutable && !is_mutable(cx, caller) { |
| if let Some(hir_id) = path_to_local_with_projections(caller) |
| && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) |
| && let PatKind::Binding(_, _, ident, _) = pat.kind |
| { |
| // We can reach the binding -- suggest making it mutable |
| let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; |
| |
| let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); |
| |
| diag.multipart_suggestion( |
| format!("remove the call to `{name}`, and make `{ident}` mutable"), |
| suggs, |
| app, |
| ); |
| } else { |
| // If we can't make the binding mutable, prevent the suggestion from being automatically applied, |
| // and add a complementary help message. |
| app = Applicability::Unspecified; |
| |
| let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) |
| && let ExprKind::MethodCall(method, ..) = expr.kind |
| { |
| Some(method.ident) |
| } else { |
| None |
| }; |
| |
| diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); |
| |
| let note = if let Some(method_requiring_mut) = method_requiring_mut { |
| format!("this must be made mutable to use `{method_requiring_mut}`") |
| } else { |
| "this must be made mutable".to_string() |
| }; |
| diag.span_note(caller.span, note); |
| } |
| } else { |
| diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); |
| } |
| }); |
| } |
| } |