Change:
The macro is required, as is that exact order of the bounds, and the use of the repeated bound in the . (It's not specific to though.) output:
Works now, I'll check in a test case
Test case checked in in . Closing

Discussion:
}; use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg}; use if_chain::if_chain; <del> use itertools::Itertools; </del> use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability;

fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> bool { if_chain! { <del> if let ExprKind::Path(ref qpath) = expr.kind; if let QPath::Resolved(None, ref path) = *qpath; </del> <ins> if let ExprKind::Path(qpath) = &expr.kind; if let QPath::Resolved(None, path) = qpath; </ins> if path.segments.len() == 1; if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id); // our variable! if local_id == var; then { <del> return true; </del> <ins> true } else { false </ins> } } <ins> } </ins> <del> false </del> <ins> #[derive(Clone, Copy)] enum OffsetSign { Positive, Negative, </ins> } struct Offset { value: String, <del> negate: bool, </del> <ins> sign: OffsetSign, </ins> } impl Offset { <del> fn negative(s: String) -> Self { Self { value: s, negate: true } </del> <ins> fn negative(value: String) -> Self { Self { value, sign: OffsetSign::Negative, } </ins> } <del> fn positive(s: String) -> Self { </del> <ins> fn positive(value: String) -> Self { </ins> Self { <del> value: s, negate: false, </del> <ins> value, sign: OffsetSign::Positive, </ins> } } }

is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) } <del> fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option<FixedOffsetVar> { </del> <ins> fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { if_chain! { if let ExprKind::MethodCall(method, _, args) = expr.kind; if method.ident.name == sym!(clone); if args.len() == 1; if let Some(arg) = args.get(0); then { arg } else { expr } } } fn get_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, idx: &Expr<'_>, var: HirId) -> Option<Offset> { </ins> fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option<String> { <del> match e.kind { ExprKind::Lit(ref l) => match l.node { </del> <ins> match &e.kind { ExprKind::Lit(l) => match l.node { </ins> ast::LitKind::Int(x, _ty) => Some(x.to_string()), _ => None, },

} } <del> if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind { let ty = cx.tables.expr_ty(seqexpr); if !is_slice_like(cx, ty) { return None; } let offset = match idx.kind { ExprKind::Binary(op, ref lhs, ref rhs) => match op.node { BinOpKind::Add => { let offset_opt = if same_var(cx, lhs, var) { extract_offset(cx, rhs, var) } else if same_var(cx, rhs, var) { extract_offset(cx, lhs, var) } else { None }; offset_opt.map(Offset::positive) }, BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative), _ => None, }, ExprKind::Path(..) => { if same_var(cx, idx, var) { Some(Offset::positive("0".into())) </del> <ins> match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { let offset_opt = if same_var(cx, lhs, var) { extract_offset(cx, rhs, var) } else if same_var(cx, rhs, var) { extract_offset(cx, lhs, var) </ins> } else { None <del> } </del> <ins> }; offset_opt.map(Offset::positive) </ins> }, <ins> BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative), </ins> _ => None, <del> }; offset.map(|o| FixedOffsetVar { var_name: snippet_opt(cx, seqexpr.span).unwrap_or_else(|| "???".into()), offset: o, }) } else { None } } fn fetch_cloned_fixed_offset_var<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId, ) -> Option<FixedOffsetVar> { if_chain! { if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind; if method.ident.name == sym!(clone); if args.len() == 1; if let Some(arg) = args.get(0); then { return get_fixed_offset_var(cx, arg, var); } </del> <ins> }, ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())), _ => None, </ins> } <del> get_fixed_offset_var(cx, expr, var) </del> } <del> fn get_indexed_assignments<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, body: &Expr<'_>, var: HirId, ) -> Vec<(FixedOffsetVar, FixedOffsetVar)> { fn get_assignment<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId, ) -> Option<(FixedOffsetVar, FixedOffsetVar)> { if let ExprKind::Assign(ref lhs, ref rhs, _) = e.kind { match ( get_fixed_offset_var(cx, lhs, var), fetch_cloned_fixed_offset_var(cx, rhs, var), ) { (Some(offset_left), Some(offset_right)) => { // Source and destination must be different if offset_left.var_name == offset_right.var_name { None } else { Some((offset_left, offset_right)) } }, _ => None, } </del> <ins> fn get_assignments<'a, 'tcx>( body: &'tcx Expr<'tcx>, ) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> { fn get_assignment<'a, 'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { if let ExprKind::Assign(lhs, rhs, _) = e.kind { Some((lhs, rhs)) </ins> } else { None } } <del> if let ExprKind::Block(ref b, _) = body.kind { let Block { ref stmts, ref expr, .. } = **b; </del> <ins> // This is one of few ways to return different iterators // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 let mut iter_a = None; let mut iter_b = None; if let ExprKind::Block(b, _) = body.kind { let Block { stmts, expr, .. } = *b; </ins> <del> stmts </del> <ins> iter_a = stmts </ins> .iter() <del> .map(|stmt| match stmt.kind { </del> <ins> .filter_map(|stmt| match stmt.kind { </ins> StmtKind::Local(..) | StmtKind::Item(..) => None, <del> StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => Some(get_assignment(cx, e, var)), </del> <ins> StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), </ins> }) <del> .chain(expr.as_ref().into_iter().map(|e| Some(get_assignment(cx, &*e, var)))) .filter_map(|op| op) .collect::<Option<Vec<_>>>() .unwrap_or_default() </del> <ins> .chain(expr.into_iter()) .map(get_assignment) .into() </ins> } else { <del> get_assignment(cx, body, var).into_iter().collect() </del> <ins> iter_b = Some(get_assignment(body)) </ins> } <ins> iter_a.into_iter().flatten().chain(iter_b.into_iter()) </ins> } /// Checks for for loops that sequentially copy items from one slice-like

) { if let Some(higher::Range { start: Some(start), <del> ref end, </del> <ins> end: Some(end), </ins> limits, }) = higher::range(cx, arg) { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { <del> let print_sum = |arg1: &Offset, arg2: &Offset| -> String { match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) { ("0", _, "0", _) => "".into(), ("0", _, x, false) | (x, false, "0", false) => x.into(), ("0", _, x, true) | (x, false, "0", true) => format!("-{}", x), (x, false, y, false) => format!("({} + {})", x, y), (x, false, y, true) => { </del> <ins> fn print_sum(arg1: &str, arg2: &Offset) -> String { match (arg1, &arg2.value[..], arg2.sign) { ("0", "0", _) => "0".into(), ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(), ("0", x, OffsetSign::Negative) => format!("-{}", x), (x, y, OffsetSign::Positive) => format!("({} + {})", x, y), (x, y, OffsetSign::Negative) => { </ins> if x == y { "0".into() } else { format!("({} - {})", x, y) } }, <del> (x, true, y, false) => { if x == y { "0".into() } else { format!("({} - {})", y, x) } }, (x, true, y, true) => format!("-({} + {})", x, y), </del> } <del> }; </del> <ins> } </ins> <del> let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| { if let Some(end) = *end { if_chain! { if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind; if method.ident.name == sym!(len); if len_args.len() == 1; if let Some(arg) = len_args.get(0); if snippet(cx, arg.span, "??") == var_name; then { return if offset.negate { format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value) } else { String::new() }; } } </del> <ins> fn print_offset(start_str: &str, inline_offset: &Offset) -> String { let offset = print_sum(start_str, inline_offset); if offset.as_str() == "0" { "".into() } else { offset } } </ins> <del> let end_str = match limits { ast::RangeLimits::Closed => { let end = sugg::Sugg::hir(cx, end, "<count>"); format!("{}", end + sugg::ONE) }, ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), }; </del> <ins> let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { if_chain! { if let ExprKind::MethodCall(method, _, len_args) = end.kind; if method.ident.name == sym!(len); if len_args.len() == 1; if let Some(arg) = len_args.get(0); if snippet(cx, arg.span, "??") == var_name; then { match offset.sign { OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value), OffsetSign::Positive => "".into(), } } else { let end_str = match limits { ast::RangeLimits::Closed => { let end = sugg::Sugg::hir(cx, end, "<count>"); format!("{}", end + sugg::ONE) }, ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), }; </ins> <del> print_sum(&Offset::positive(end_str), &offset) } else { "..".into() </del> <ins> print_sum(&end_str, &offset) } </ins> } }; // The only statements in the for loops can be indexed assignments from // indexed retrievals. <del> let manual_copies = get_indexed_assignments(cx, body, canonical_id); let big_sugg = manual_copies .into_iter() .map(|(dst_var, src_var)| { let start_str = Offset::positive(snippet(cx, start.span, "").to_string()); let dst_offset = print_sum(&start_str, &dst_var.offset); let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name); let src_offset = print_sum(&start_str, &src_var.offset); let src_limit = print_limit(end, src_var.offset, &src_var.var_name); let dst = if dst_offset == "" && dst_limit == "" { dst_var.var_name } else { format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit) }; </del> <ins> let big_sugg = get_assignments(body) .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); if_chain! { if let ExprKind::Index(seqexpr_left, idx_left) = lhs.kind; if let ExprKind::Index(seqexpr_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.tables.expr_ty(seqexpr_left)) && is_slice_like(cx, cx.tables.expr_ty(seqexpr_right)); if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id); if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id); let var_name_left = snippet_opt(cx, seqexpr_left.span).unwrap_or_else(|| "???".into()); let var_name_right = snippet_opt(cx, seqexpr_right.span).unwrap_or_else(|| "???".into()); // Source and destination must be different if var_name_left != var_name_right; then { Some((FixedOffsetVar { var_name: var_name_left, offset: offset_left }, FixedOffsetVar { var_name: var_name_right, offset: offset_right })) } else { None } } }) }) .map(|o| { o.map(|(dst_var, src_var)| { let start_str = snippet(cx, start.span, "").to_string(); let dst_offset = print_offset(&start_str, &dst_var.offset); let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name); let src_offset = print_offset(&start_str, &src_var.offset); let src_limit = print_limit(end, src_var.offset, &src_var.var_name); let dst = if dst_offset == "" && dst_limit == "" { dst_var.var_name } else { format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit) }; </ins> <del> format!( "{}.clone_from_slice(&{}[{}..{}])", dst, src_var.var_name, src_offset, src_limit ) </del> <ins> format!( "{}.clone_from_slice(&{}[{}..{}])", dst, src_var.var_name, src_offset, src_limit ) }) </ins> }) <del> .join("n    "); </del> <ins> .collect::<Option<Vec<_>>>() .filter(|v| !v.is_empty()) .map(|v| v.join("n    ")); </ins> <del> if !big_sugg.is_empty() { </del> <ins> if let Some(big_sugg) = big_sugg { </ins> span_lint_and_sugg( cx, MANUAL_MEMCPY,

for i in from..from + 3 { dst[i] = src[i - from]; } <ins> // `RangeTo` `for` loop - don't trigger lint for i in 0.. { dst[i] = src[i]; } </ins> } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)]

--> $DIR/manual_memcpy.rs:94:14 | LL |     for i in from..from + src.len() { <del> |              ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[0..(from + src.len() - from)])` </del> <ins> |              ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[..(from + src.len() - from)])` </ins> error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:98:14 | LL |     for i in from..from + 3 { <del> |              ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[0..(from + 3 - from)])` </del> <ins> |              ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` </ins> error: it looks like you're manually copying between slices <del> --> $DIR/manual_memcpy.rs:105:14 </del> <ins> --> $DIR/manual_memcpy.rs:110:14 </ins> | LL |     for i in 0..src.len() { |              ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])`