Skip to content

Commit 5cb42db

Browse files
Rollup merge of rust-lang#149027 - reddevilmidzy:suggest, r=madsmtm
Improve cross-crate trait impl param mismatch suggestions resolve: rust-lang#106999
2 parents f993e2f + 97748ad commit 5cb42db

7 files changed

Lines changed: 358 additions & 1 deletion

File tree

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use core::ops::ControlFlow;
22
use std::borrow::Cow;
3+
use std::cmp::Ordering;
34
use std::iter;
45

56
use hir::def_id::{DefId, DefIdMap, LocalDefId};
@@ -18,7 +19,7 @@ use rustc_middle::ty::{
1819
Upcast,
1920
};
2021
use rustc_middle::{bug, span_bug};
21-
use rustc_span::{DUMMY_SP, Span};
22+
use rustc_span::{BytePos, DUMMY_SP, Span};
2223
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
2324
use rustc_trait_selection::infer::InferCtxtExt;
2425
use rustc_trait_selection::regions::InferCtxtRegionExt;
@@ -1795,6 +1796,105 @@ fn compare_number_of_method_arguments<'tcx>(
17951796
),
17961797
);
17971798

1799+
// Only emit verbose suggestions when the trait span isn’t local (e.g., cross-crate).
1800+
if !trait_m.def_id.is_local() {
1801+
let trait_sig = tcx.fn_sig(trait_m.def_id);
1802+
let trait_arg_idents = tcx.fn_arg_idents(trait_m.def_id);
1803+
let sm = tcx.sess.source_map();
1804+
// Find the span of the space between the parentheses in a method.
1805+
// fn foo(...) {}
1806+
// ^^^
1807+
let impl_inputs_span = if let (Some(first), Some(last)) =
1808+
(impl_m_sig.decl.inputs.first(), impl_m_sig.decl.inputs.last())
1809+
{
1810+
// We have inputs; construct the span from those.
1811+
// fn foo( a: i32, b: u32 ) {}
1812+
// ^^^^^^^^^^^^^^^^
1813+
let arg_idents = tcx.fn_arg_idents(impl_m.def_id);
1814+
let first_lo = arg_idents
1815+
.get(0)
1816+
.and_then(|id| id.map(|id| id.span.lo()))
1817+
.unwrap_or(first.span.lo());
1818+
Some(impl_m_sig.span.with_lo(first_lo).with_hi(last.span.hi()))
1819+
} else {
1820+
// We have no inputs; construct the span to the left of the last parenthesis
1821+
// fn foo( ) {}
1822+
// ^
1823+
// FIXME: Keep spans for function parentheses around to make this more robust.
1824+
sm.span_to_snippet(impl_m_sig.span).ok().and_then(|s| {
1825+
let right_paren = s.as_bytes().iter().rposition(|&b| b == b')')?;
1826+
let pos = impl_m_sig.span.lo() + BytePos(right_paren as u32);
1827+
Some(impl_m_sig.span.with_lo(pos).with_hi(pos))
1828+
})
1829+
};
1830+
let suggestion = match trait_number_args.cmp(&impl_number_args) {
1831+
Ordering::Greater => {
1832+
// Span is right before the end parenthesis:
1833+
// fn foo(a: i32 ) {}
1834+
// ^
1835+
let trait_inputs = trait_sig.skip_binder().inputs().skip_binder();
1836+
let missing = trait_inputs
1837+
.iter()
1838+
.enumerate()
1839+
.skip(impl_number_args)
1840+
.map(|(idx, ty)| {
1841+
let name = trait_arg_idents
1842+
.get(idx)
1843+
.and_then(|ident| *ident)
1844+
.map(|ident| ident.to_string())
1845+
.unwrap_or_else(|| "_".to_string());
1846+
format!("{name}: {ty}")
1847+
})
1848+
.collect::<Vec<_>>();
1849+
1850+
if missing.is_empty() {
1851+
None
1852+
} else {
1853+
impl_inputs_span.map(|s| {
1854+
let span = s.shrink_to_hi();
1855+
let prefix = if impl_number_args == 0 { "" } else { ", " };
1856+
let replacement = format!("{prefix}{}", missing.join(", "));
1857+
(
1858+
span,
1859+
format!(
1860+
"add the missing parameter{} from the trait",
1861+
pluralize!(trait_number_args - impl_number_args)
1862+
),
1863+
replacement,
1864+
)
1865+
})
1866+
}
1867+
}
1868+
Ordering::Less => impl_inputs_span.and_then(|full| {
1869+
// Span of the arguments that there are too many of:
1870+
// fn foo(a: i32, b: u32) {}
1871+
// ^^^^^^^^
1872+
let lo = if trait_number_args == 0 {
1873+
full.lo()
1874+
} else {
1875+
impl_m_sig
1876+
.decl
1877+
.inputs
1878+
.get(trait_number_args - 1)
1879+
.map(|arg| arg.span.hi())?
1880+
};
1881+
let span = full.with_lo(lo);
1882+
Some((
1883+
span,
1884+
format!(
1885+
"remove the extra parameter{} to match the trait",
1886+
pluralize!(impl_number_args - trait_number_args)
1887+
),
1888+
String::new(),
1889+
))
1890+
}),
1891+
Ordering::Equal => unreachable!(),
1892+
};
1893+
if let Some((span, msg, replacement)) = suggestion {
1894+
err.span_suggestion_verbose(span, msg, replacement, Applicability::MaybeIncorrect);
1895+
}
1896+
}
1897+
17981898
return Err(err.emit_unless_delay(delay));
17991899
}
18001900

tests/ui/fn/issue-39259.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ LL | fn call(&self) -> u32 {
1717
| ^^^^^ expected 2 parameters, found 1
1818
|
1919
= note: `call` from trait: `extern "rust-call" fn(&Self, Args) -> <Self as FnOnce<Args>>::Output`
20+
help: add the missing parameter from the trait
21+
|
22+
LL | fn call(&self, args: Args) -> u32 {
23+
| ++++++++++++
2024

2125
error[E0277]: expected a `FnMut(u32)` closure, found `S`
2226
--> $DIR/issue-39259.rs:6:25

tests/ui/impl-trait/trait_type.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ LL | fn fmt(&self) -> () { }
1919
| ^^^^^ expected 2 parameters, found 1
2020
|
2121
= note: `fmt` from trait: `fn(&Self, &mut Formatter<'_>) -> Result<(), std::fmt::Error>`
22+
help: add the missing parameter from the trait
23+
|
24+
LL | fn fmt(&self, f: &mut Formatter<'_>) -> () { }
25+
| +++++++++++++++++++++++
2226

2327
error[E0186]: method `fmt` has a `&self` declaration in the trait, but not in the impl
2428
--> $DIR/trait_type.rs:17:4
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ edition:2018
2+
pub trait Foo {
3+
fn foo(a: i32, b: u32);
4+
}
5+
6+
pub trait Bar {
7+
fn no_params();
8+
fn has_self(&self);
9+
fn has_two_self(self: &Self, other: &Self);
10+
}
11+
12+
pub trait Baz<T> {
13+
fn generic_no_params<U: Baz<()>>();
14+
fn generic_one_param<U: Baz<()>>(a: U);
15+
}
16+
17+
pub trait NonIdentArguments {
18+
fn pattern_types_in_arguments(_: i32, (_a, _b): (i32, i32)) {}
19+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//@ run-rustfix
2+
//@ aux-build:trait-impl-params.rs
3+
//@ edition:2018
4+
//! Tests cross-crate trait impl param mismatch suggestions (keep aliases when matching,
5+
//! append missing; replace otherwise).
6+
//! regression test for https://github.com/rust-lang/rust/issues/106999
7+
8+
#![allow(unused)]
9+
extern crate trait_impl_params;
10+
11+
use trait_impl_params::{Bar, Baz, Foo, NonIdentArguments};
12+
13+
struct MyStruct1;
14+
impl Foo for MyStruct1 {
15+
fn foo(a: i32, b: u32) {}
16+
//~^ ERROR: method `foo` has 0 parameters but the declaration in trait `foo` has 2
17+
//~| HELP: add the missing parameters from the trait
18+
}
19+
20+
struct MyStruct2;
21+
type AliasOfI32 = i32;
22+
impl Foo for MyStruct2 {
23+
fn foo(a: AliasOfI32, b: u32) {}
24+
//~^ ERROR: method `foo` has 1 parameter but the declaration in trait `foo` has 2
25+
//~| HELP: add the missing parameter from the trait
26+
}
27+
28+
struct MyStruct3;
29+
type AliasOfU32 = u32;
30+
impl Foo for MyStruct3 {
31+
fn foo(a: AliasOfI32, b: AliasOfU32) {} // Ok
32+
}
33+
34+
struct MyStruct4;
35+
impl Bar for MyStruct4 {
36+
fn no_params() {}
37+
//~^ ERROR: method `no_params` has 1 parameter but the declaration in trait `no_params` has 0
38+
//~| HELP: remove the extra parameter to match the trait
39+
fn has_self(&self) {}
40+
//~^ ERROR: method `has_self` has 2 parameters but the declaration in trait `has_self` has 1
41+
//~| HELP: remove the extra parameter to match the trait
42+
fn has_two_self(self: &Self, other: &Self) {}
43+
//~^ ERROR: method `has_two_self` has 1 parameter but the declaration in trait `has_two_self` has 2
44+
//~| HELP: add the missing parameter from the trait
45+
}
46+
47+
struct MyStruct5;
48+
impl NonIdentArguments for MyStruct5 {
49+
fn pattern_types_in_arguments(_: i32, _: (i32, i32)) {}
50+
//~^ ERROR: method `pattern_types_in_arguments` has 0 parameters but the declaration in trait
51+
//~| HELP: add the missing parameters from the trait
52+
}
53+
54+
struct MyStruct6;
55+
impl Baz<()> for MyStruct6 {
56+
fn generic_no_params<U: Baz<()>>() {}
57+
//~^ ERROR: method `generic_no_params` has 1 parameter but the declaration in trait
58+
//~| HELP: remove the extra parameter to match the trait
59+
fn generic_one_param<U: Baz<()>>(a: U) {}
60+
//~^ ERROR: method `generic_one_param` has 0 parameters but the declaration in trait
61+
//~| HELP: add the missing parameter from the trait
62+
}
63+
64+
fn main() {}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//@ run-rustfix
2+
//@ aux-build:trait-impl-params.rs
3+
//@ edition:2018
4+
//! Tests cross-crate trait impl param mismatch suggestions (keep aliases when matching,
5+
//! append missing; replace otherwise).
6+
//! regression test for https://github.com/rust-lang/rust/issues/106999
7+
8+
#![allow(unused)]
9+
extern crate trait_impl_params;
10+
11+
use trait_impl_params::{Bar, Baz, Foo, NonIdentArguments};
12+
13+
struct MyStruct1;
14+
impl Foo for MyStruct1 {
15+
fn foo() {}
16+
//~^ ERROR: method `foo` has 0 parameters but the declaration in trait `foo` has 2
17+
//~| HELP: add the missing parameters from the trait
18+
}
19+
20+
struct MyStruct2;
21+
type AliasOfI32 = i32;
22+
impl Foo for MyStruct2 {
23+
fn foo(a: AliasOfI32) {}
24+
//~^ ERROR: method `foo` has 1 parameter but the declaration in trait `foo` has 2
25+
//~| HELP: add the missing parameter from the trait
26+
}
27+
28+
struct MyStruct3;
29+
type AliasOfU32 = u32;
30+
impl Foo for MyStruct3 {
31+
fn foo(a: AliasOfI32, b: AliasOfU32) {} // Ok
32+
}
33+
34+
struct MyStruct4;
35+
impl Bar for MyStruct4 {
36+
fn no_params(x: i32) {}
37+
//~^ ERROR: method `no_params` has 1 parameter but the declaration in trait `no_params` has 0
38+
//~| HELP: remove the extra parameter to match the trait
39+
fn has_self(&self, y: i32) {}
40+
//~^ ERROR: method `has_self` has 2 parameters but the declaration in trait `has_self` has 1
41+
//~| HELP: remove the extra parameter to match the trait
42+
fn has_two_self(self: &Self) {}
43+
//~^ ERROR: method `has_two_self` has 1 parameter but the declaration in trait `has_two_self` has 2
44+
//~| HELP: add the missing parameter from the trait
45+
}
46+
47+
struct MyStruct5;
48+
impl NonIdentArguments for MyStruct5 {
49+
fn pattern_types_in_arguments() {}
50+
//~^ ERROR: method `pattern_types_in_arguments` has 0 parameters but the declaration in trait
51+
//~| HELP: add the missing parameters from the trait
52+
}
53+
54+
struct MyStruct6;
55+
impl Baz<()> for MyStruct6 {
56+
fn generic_no_params<U: Baz<()>>(a: U) {}
57+
//~^ ERROR: method `generic_no_params` has 1 parameter but the declaration in trait
58+
//~| HELP: remove the extra parameter to match the trait
59+
fn generic_one_param<U: Baz<()>>() {}
60+
//~^ ERROR: method `generic_one_param` has 0 parameters but the declaration in trait
61+
//~| HELP: add the missing parameter from the trait
62+
}
63+
64+
fn main() {}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
error[E0050]: method `foo` has 0 parameters but the declaration in trait `foo` has 2
2+
--> $DIR/trait-impl-param-mismatched-cross-crate.rs:15:5
3+
|
4+
LL | fn foo() {}
5+
| ^^^^^^^^ expected 2 parameters, found 0
6+
|
7+
= note: `foo` from trait: `fn(i32, u32)`
8+
help: add the missing parameters from the trait
9+
|
10+
LL | fn foo(a: i32, b: u32) {}
11+
| ++++++++++++++
12+
13+
error[E0050]: method `foo` has 1 parameter but the declaration in trait `foo` has 2
14+
--> $DIR/trait-impl-param-mismatched-cross-crate.rs:23:15
15+
|
16+
LL | fn foo(a: AliasOfI32) {}
17+
| ^^^^^^^^^^ expected 2 parameters, found 1
18+
|
19+
= note: `foo` from trait: `fn(i32, u32)`
20+
help: add the missing parameter from the trait
21+
|
22+
LL | fn foo(a: AliasOfI32, b: u32) {}
23+
| ++++++++
24+
25+
error[E0050]: method `no_params` has 1 parameter but the declaration in trait `no_params` has 0
26+
--> $DIR/trait-impl-param-mismatched-cross-crate.rs:36:21
27+
|
28+
LL | fn no_params(x: i32) {}
29+
| ^^^ expected 0 parameters, found 1
30+
|
31+
= note: `no_params` from trait: `fn()`
32+
help: remove the extra parameter to match the trait
33+
|
34+
LL - fn no_params(x: i32) {}
35+
LL + fn no_params() {}
36+
|
37+
38+
error[E0050]: method `has_self` has 2 parameters but the declaration in trait `has_self` has 1
39+
--> $DIR/trait-impl-param-mismatched-cross-crate.rs:39:17
40+
|
41+
LL | fn has_self(&self, y: i32) {}
42+
| ^^^^^^^^^^^^^ expected 1 parameter, found 2
43+
|
44+
= note: `has_self` from trait: `fn(&Self)`
45+
help: remove the extra parameter to match the trait
46+
|
47+
LL - fn has_self(&self, y: i32) {}
48+
LL + fn has_self(&self) {}
49+
|
50+
51+
error[E0050]: method `has_two_self` has 1 parameter but the declaration in trait `has_two_self` has 2
52+
--> $DIR/trait-impl-param-mismatched-cross-crate.rs:42:27
53+
|
54+
LL | fn has_two_self(self: &Self) {}
55+
| ^^^^^ expected 2 parameters, found 1
56+
|
57+
= note: `has_two_self` from trait: `fn(&Self, &Self)`
58+
help: add the missing parameter from the trait
59+
|
60+
LL | fn has_two_self(self: &Self, other: &Self) {}
61+
| ++++++++++++++
62+
63+
error[E0050]: method `pattern_types_in_arguments` has 0 parameters but the declaration in trait `pattern_types_in_arguments` has 2
64+
--> $DIR/trait-impl-param-mismatched-cross-crate.rs:49:5
65+
|
66+
LL | fn pattern_types_in_arguments() {}
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 parameters, found 0
68+
|
69+
= note: `pattern_types_in_arguments` from trait: `fn(i32, (i32, i32))`
70+
help: add the missing parameters from the trait
71+
|
72+
LL | fn pattern_types_in_arguments(_: i32, _: (i32, i32)) {}
73+
| +++++++++++++++++++++
74+
75+
error[E0050]: method `generic_no_params` has 1 parameter but the declaration in trait `generic_no_params` has 0
76+
--> $DIR/trait-impl-param-mismatched-cross-crate.rs:56:41
77+
|
78+
LL | fn generic_no_params<U: Baz<()>>(a: U) {}
79+
| ^ expected 0 parameters, found 1
80+
|
81+
= note: `generic_no_params` from trait: `fn()`
82+
help: remove the extra parameter to match the trait
83+
|
84+
LL - fn generic_no_params<U: Baz<()>>(a: U) {}
85+
LL + fn generic_no_params<U: Baz<()>>() {}
86+
|
87+
88+
error[E0050]: method `generic_one_param` has 0 parameters but the declaration in trait `generic_one_param` has 1
89+
--> $DIR/trait-impl-param-mismatched-cross-crate.rs:59:5
90+
|
91+
LL | fn generic_one_param<U: Baz<()>>() {}
92+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 parameter, found 0
93+
|
94+
= note: `generic_one_param` from trait: `fn(U)`
95+
help: add the missing parameter from the trait
96+
|
97+
LL | fn generic_one_param<U: Baz<()>>(a: U) {}
98+
| ++++
99+
100+
error: aborting due to 8 previous errors
101+
102+
For more information about this error, try `rustc --explain E0050`.

0 commit comments

Comments
 (0)