Repository: rust-lang/rust
File: compiler/rustc_hir_analysis/src/check/compare_impl_item.rs (Rust)

Code before:
            GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)),
            GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None,
        });
    let trait_m_type_params =
        trait_m_generics.own_params.iter().filter_map(|param| match param.kind {
            GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)),
            GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None,
        });
    for ((impl_def_id, impl_synthetic), (trait_def_id, trait_synthetic)) in
        iter::zip(impl_m_type_params, trait_m_type_params)
    {
        if impl_synthetic != trait_synthetic {
            let impl_def_id = impl_def_id.expect_local();
            let impl_span = tcx.def_span(impl_def_id);
            let trait_span = tcx.def_span(trait_def_id);
            let mut err = struct_span_code_err!(
                tcx.dcx(),
                impl_span,
                E0643,
                "method `{}` has incompatible signature for trait",
                trait_m.name()
            );
            err.span_label(trait_span, "declaration in trait here");
            if impl_synthetic {
                // The case where the impl method uses `impl Trait` but the trait method uses
                // explicit generics
                err.span_label(impl_span, "expected generic parameter, found `impl Trait`");
                let _: Option<_> = try {
                    // try taking the name from the trait impl
                    // FIXME: this is obviously suboptimal since the name can already be used
                    // as another generic argument
                    let new_name = tcx.opt_item_name(trait_def_id)?;
                    let trait_m = trait_m.def_id.as_local()?;
                    let trait_m = tcx.hir_expect_trait_item(trait_m);

                    let impl_m = impl_m.def_id.as_local()?;
                    let impl_m = tcx.hir_expect_impl_item(impl_m);

                    // in case there are no generics, take the spot between the function name
                    // and the opening paren of the argument list
                    let new_generics_span = tcx.def_ident_span(impl_def_id)?.shrink_to_hi();
                    // in case there are generics, just replace them
                    let generics_span = impl_m.generics.span.substitute_dummy(new_generics_span);
                    // replace with the generics from the trait
                    let new_generics =
                        tcx.sess.source_map().span_to_snippet(trait_m.generics.span).ok()?;

                    err.multipart_suggestion(
                        "try changing the `impl Trait` argument to a generic parameter",
                        vec![
                            // replace `impl Trait` with `T`

Review comment (suggestion):
Instead of always making the span the entire argument list, maybe you can shrink it to only be the relevant parts?

Something like:
```rust
let (span, replacement) = if trait_number_args > impl_number_args {
    // Span is right before the end parenthesis:
    // fn foo(a: i32 ) {}
    //              ^
    let span = impl_inputs_span.shrink_to_hi();
    let replacement = ", b: u32";
    (span, replacement)
} else {
    // Span of the arguments that there are too many of:
    // fn foo(a: i32, b: u32) {}
    //              ^^^^^^^^
    let span = impl_inputs_span.with_lo(impl_m_sig.decl.inputs[trait_number_args - 1].span);
    let replacement = "";
    (span, replacement)
};
```


Code after:
                    // Span of the arguments that there are too many of:
                    // fn foo(a: i32, b: u32) {}
                    //              ^^^^^^^^
                    let lo = if trait_number_args == 0 {
                        full.lo()
                    } else {
                        impl_m_sig
                            .decl
                            .inputs
                            .get(trait_number_args - 1)
                            .map(|arg| arg.span.hi())?
                    };
                    let span = full.with_lo(lo);
                    Some((
                        span,
                        format!(
                            "remove the extra parameter{} to match the trait",
                            pluralize!(impl_number_args - trait_number_args)
                        ),
                        String::new(),
                    ))
                }),
                Ordering::Equal => unreachable!(),
            };
            if let Some((span, msg, replacement)) = suggestion {
                err.span_suggestion_verbose(span, msg, replacement, Applicability::MaybeIncorrect);
            }
        }

        return Err(err.emit_unless_delay(delay));
    }

    Ok(())
}

fn compare_synthetic_generics<'tcx>(
    tcx: TyCtxt<'tcx>,
    impl_m: ty::AssocItem,
    trait_m: ty::AssocItem,
    delay: bool,
) -> Result<(), ErrorGuaranteed> {
    // FIXME(chrisvittal) Clean up this function, list of FIXME items:
    //     1. Better messages for the span labels
    //     2. Explanation as to what is going on
    // If we get here, we already have the same number of generics, so the zip will
    // be okay.
    let mut error_found = None;
    let impl_m_generics = tcx.generics_of(impl_m.def_id);
    let trait_m_generics = tcx.generics_of(trait_m.def_id);
    let impl_m_type_params =
        impl_m_generics.own_params.iter().filter_map(|param| match param.kind {