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

Code before:

        err.span_label(
            impl_span,
            format!(
                "expected {}, found {}",
                potentially_plural_count(trait_number_args, "parameter"),
                impl_number_args
            ),
        );

        // Only emit verbose suggestions when the trait span isn’t local (e.g., cross-crate).
        if trait_span.is_none() {
            let trait_sig = tcx.fn_sig(trait_m.def_id);
            let trait_arg_idents = tcx.fn_arg_idents(trait_m.def_id);
            let sm = tcx.sess.source_map();
            let impl_inputs_span = (!impl_m_sig.span.is_dummy())
                .then(|| {
                    sm.span_to_snippet(impl_m_sig.span).ok().and_then(|snippet| {
                        find_param_bounds(&snippet).map(|(lo_rel, hi_rel)| {
                            let lo = impl_m_sig.span.lo() + BytePos(lo_rel as u32);
                            let hi = impl_m_sig.span.lo() + BytePos(hi_rel as u32);
                            impl_m_sig.span.with_lo(lo).with_hi(hi)
                        })
                    })
                })
                .flatten();

            let suggestion_text = if trait_number_args > impl_number_args {
                let existing = impl_inputs_span
                    .and_then(|span| sm.span_to_snippet(span).ok())
                    .unwrap_or_default();

                let trait_inputs = trait_sig.skip_binder().inputs().skip_binder();
                let missing = trait_inputs
                    .iter()
                    .enumerate()
                    .skip(impl_number_args)
                    .map(|(idx, ty)| {
                        let name = trait_arg_idents
                            .get(idx)
                            .and_then(|ident| *ident)
                            .map(|ident| ident.to_string())
                            .unwrap_or_else(|| format!("_{}", idx + 1));
                        format!("{name}: {ty}")
                    })
                    .collect::<Vec<_>>();

                let replacement = if existing.trim().is_empty() {
                    missing.join(", ")
                } else {
                    format!("{existing}, {}", missing.join(", "))

Review comment (bug):
I feel like inspecting the source and looking for parentheses with `find_param_bounds` is brittle. For example, I think it would find the wrong span on something like:
```rust
trait Foo<T> {}
fn foo<T: Foo<()>>() {}
```

That said, I recognize that it's gonna be annoying to find this span in any case, because the span of the parentheses that enclose parameters seems to be dropped early in the compilation pipeline (it's not even part of `ast::Fn`).

Maybe a more robust approach would be to:
- If there are parameters, grab the span from the first parameter identifier and last parameter type, and combine them.
- If there are no parameters, grab the span of the output, and walk backwards until we find `)` and `(`.

In any case, maybe you could inline `find_param_bounds`, that might make it easier to follow what's going on?

And maybe you could add a comment about what it is that we're actually looking for? Something like:
```rust
// Find the span of the space between the parentheses in a method.
// fn foo(...) {}
//        ^^^
```

Code after:
        let impl_span = impl_m_sig
            .decl
            .inputs
            .get(pos)
            .map(|arg| {
                if pos == 0 {
                    arg.span
                } else {
                    arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo())
                }
            })
            .unwrap_or_else(|| tcx.def_span(impl_m.def_id));

        let mut err = struct_span_code_err!(
            tcx.dcx(),
            impl_span,
            E0050,
            "method `{}` has {} but the declaration in trait `{}` has {}",
            trait_m.name(),
            potentially_plural_count(impl_number_args, "parameter"),
            tcx.def_path_str(trait_m.def_id),
            trait_number_args
        );

        if let Some(trait_span) = trait_span {
            err.span_label(
                trait_span,
                format!(
                    "trait requires {}",
                    potentially_plural_count(trait_number_args, "parameter")
                ),
            );
        } else {
            err.note_trait_signature(trait_m.name(), trait_m.signature(tcx));
        }

        err.span_label(
            impl_span,
            format!(
                "expected {}, found {}",
                potentially_plural_count(trait_number_args, "parameter"),
                impl_number_args
            ),
        );

        // Only emit verbose suggestions when the trait span isn’t local (e.g., cross-crate).
        if !trait_m.def_id.is_local() {
            let trait_sig = tcx.fn_sig(trait_m.def_id);
            let trait_arg_idents = tcx.fn_arg_idents(trait_m.def_id);
            let sm = tcx.sess.source_map();
            // Find the span of the space between the parentheses in a method.