-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
- fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
- // call to .map()
- if let ExprKind::MethodCall(ref method, _, ref args) = expr.node {
- if method.ident.name == "map" && args.len() == 2 {
- match args[1].node {
- ExprKind::Closure(_, ref decl, closure_eid, _, _) => {
- let body = cx.tcx.hir.body(closure_eid);
- let closure_expr = remove_blocks(&body.value);
- if_chain! {
- // nothing special in the argument, besides reference bindings
- // (e.g. .map(|&x| x) )
- if let Some(first_arg) = iter_input_pats(decl, body).next();
- if let Some(arg_ident) = get_arg_ident(&first_arg.pat);
- // the method is being called on a known type (option or iterator)
- if let Some(type_name) = get_type_name(cx, expr, &args[0]);
- then {
- // We know that body.arguments is not empty at this point
- let ty = cx.tables.pat_ty(&body.arguments[0].pat);
- // look for derefs, for .map(|x| *x)
- if only_derefs(cx, &*closure_expr, arg_ident) &&
- // .cloned() only removes one level of indirection, don't lint on more
- walk_ptrs_ty_depth(cx.tables.pat_ty(&first_arg.pat)).1 == 1
- {
- // the argument is not an &mut T
- if let ty::TyRef(_, _, mutbl) = ty.sty {
- if mutbl == MutImmutable {
- span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
- "you seem to be using .map() to clone the contents of an {}, consider \
- using `.cloned()`", type_name),
- &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
- }
- }
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapClone {
+ fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
+ if in_macro(e.span) {
+ return;
+ }
+
+ if_chain! {
+ if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.node;
+ if args.len() == 2;
+ if method.ident.as_str() == "map";
+ let ty = cx.tables.expr_ty(&args[0]);
+ let is_option = match_type(cx, ty, &paths::OPTION);
+ if is_option || match_trait_method(cx, e, &paths::ITERATOR);
+ if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].node;
+ let closure_body = cx.tcx.hir().body(body_id);
+ let closure_expr = remove_blocks(&closure_body.value);
+ then {
+ match closure_body.arguments[0].pat.node {
+ hir::PatKind::Ref(ref inner, _) => if let hir::PatKind::Binding(
+ hir::BindingAnnotation::Unannotated, .., name, None
+ ) = inner.node {
+ if ident_eq(name, closure_expr) {
+ // FIXME When Iterator::copied() stabilizes we can remove is_option
+ // from here and the other lint() calls
+ lint(cx, e.span, args[0].span, is_option);
+ }
+ },
+ hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
+ match closure_expr.node {
+ hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => {
+ if ident_eq(name, inner) && !cx.tables.expr_ty(inner).is_box() {
+ lint(cx, e.span, args[0].span, is_option);