Skip to content

Commit 895f817

Browse files
committed
Implement method signature suggestion for trait mismatches error
1 parent ce63e5d commit 895f817

File tree

9 files changed

+116
-1
lines changed

9 files changed

+116
-1
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_middle::ty::{
1818
TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
1919
};
2020
use rustc_middle::{bug, span_bug};
21-
use rustc_span::{DUMMY_SP, Span};
21+
use rustc_span::{BytePos, DUMMY_SP, Span};
2222
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
2323
use rustc_trait_selection::infer::InferCtxtExt;
2424
use rustc_trait_selection::regions::InferCtxtRegionExt;
@@ -1761,6 +1761,64 @@ fn compare_number_of_method_arguments<'tcx>(
17611761
),
17621762
);
17631763

1764+
let sm = tcx.sess.source_map();
1765+
let find_param_bounds = |snippet: &str| -> Option<(usize, usize)> {
1766+
let start = snippet.find('(')?;
1767+
let mut depth = 1usize;
1768+
let bytes = snippet.as_bytes();
1769+
let mut i = start + 1;
1770+
while i < bytes.len() {
1771+
match bytes[i] as char {
1772+
'(' => depth += 1,
1773+
')' => {
1774+
depth -= 1;
1775+
if depth == 0 {
1776+
return Some((start + 1, i));
1777+
}
1778+
}
1779+
_ => {}
1780+
}
1781+
i += 1;
1782+
}
1783+
None
1784+
};
1785+
1786+
let impl_inputs_span = (!impl_m_sig.span.is_dummy())
1787+
.then(|| {
1788+
sm.span_to_snippet(impl_m_sig.span).ok().and_then(|snippet| {
1789+
find_param_bounds(&snippet).map(|(lo_rel, hi_rel)| {
1790+
let lo = impl_m_sig.span.lo() + BytePos(lo_rel as u32);
1791+
let hi = impl_m_sig.span.lo() + BytePos(hi_rel as u32);
1792+
impl_m_sig.span.with_lo(lo).with_hi(hi)
1793+
})
1794+
})
1795+
})
1796+
.flatten();
1797+
1798+
let suggestion = trait_m
1799+
.def_id
1800+
.as_local()
1801+
.and_then(|def_id| {
1802+
let (trait_sig, _) = tcx.hir_expect_trait_item(def_id).expect_fn();
1803+
sm.span_to_snippet(trait_sig.span).ok().and_then(|snippet| {
1804+
find_param_bounds(&snippet)
1805+
.map(|(lo_rel, hi_rel)| snippet[lo_rel..hi_rel].to_string())
1806+
})
1807+
})
1808+
.or_else(|| {
1809+
let signature = trait_m.signature(tcx);
1810+
find_param_bounds(&signature).map(|(lo, hi)| signature[lo..hi].trim().to_string())
1811+
});
1812+
1813+
if let (Some(span), Some(suggestion)) = (impl_inputs_span, suggestion) {
1814+
err.span_suggestion_verbose(
1815+
span,
1816+
"modify the signature to match the trait definition",
1817+
suggestion,
1818+
Applicability::MaybeIncorrect,
1819+
);
1820+
}
1821+
17641822
return Err(err.emit_unless_delay(delay));
17651823
}
17661824

tests/ui/error-codes/E0050.stderr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ LL | fn foo(&self, x: u8) -> bool;
66
...
77
LL | fn foo(&self) -> bool { true }
88
| ^^^^^ expected 2 parameters, found 1
9+
|
10+
help: modify the signature to match the trait definition
11+
|
12+
LL | fn foo(&self, x: u8) -> bool { true }
13+
| +++++++
914

1015
error[E0050]: method `bar` has 1 parameter but the declaration in trait `Foo::bar` has 4
1116
--> $DIR/E0050.rs:11:12
@@ -15,6 +20,11 @@ LL | fn bar(&self, x: u8, y: u8, z: u8);
1520
...
1621
LL | fn bar(&self) { }
1722
| ^^^^^ expected 4 parameters, found 1
23+
|
24+
help: modify the signature to match the trait definition
25+
|
26+
LL | fn bar(&self, x: u8, y: u8, z: u8) { }
27+
| +++++++++++++++++++++
1828

1929
error[E0050]: method `less` has 4 parameters but the declaration in trait `Foo::less` has 1
2030
--> $DIR/E0050.rs:12:13
@@ -24,6 +34,12 @@ LL | fn less(&self);
2434
...
2535
LL | fn less(&self, x: u8, y: u8, z: u8) { }
2636
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 parameter, found 4
37+
|
38+
help: modify the signature to match the trait definition
39+
|
40+
LL - fn less(&self, x: u8, y: u8, z: u8) { }
41+
LL + fn less(&self) { }
42+
|
2743

2844
error: aborting due to 3 previous errors
2945

tests/ui/fn/issue-39259.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ 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: modify the signature to match the trait definition
21+
|
22+
LL - fn call(&self) -> u32 {
23+
LL + fn call(&Self, Args) -> u32 {
24+
|
2025

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

tests/ui/impl-trait/in-trait/method-signature-matches.too_few.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ LL | fn come_on_a_little_more_effort(_: (), _: (), _: ()) -> impl Sized;
66
...
77
LL | fn come_on_a_little_more_effort() {}
88
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 3 parameters, found 0
9+
|
10+
help: modify the signature to match the trait definition
11+
|
12+
LL | fn come_on_a_little_more_effort(_: (), _: (), _: ()) {}
13+
| +++++++++++++++++++
914

1015
error: aborting due to 1 previous error
1116

tests/ui/impl-trait/in-trait/method-signature-matches.too_many.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ LL | fn calm_down_please() -> impl Sized;
66
...
77
LL | fn calm_down_please(_: (), _: (), _: ()) {}
88
| ^^^^^^^^^^^^^^^^ expected 0 parameters, found 3
9+
|
10+
help: modify the signature to match the trait definition
11+
|
12+
LL - fn calm_down_please(_: (), _: (), _: ()) {}
13+
LL + fn calm_down_please() {}
14+
|
915

1016
error: aborting due to 1 previous error
1117

tests/ui/impl-trait/trait_type.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ 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: modify the signature to match the trait definition
23+
|
24+
LL - fn fmt(&self) -> () { }
25+
LL + fn fmt(&Self, &mut Formatter<'_>) -> () { }
26+
|
2227

2328
error[E0186]: method `fmt` has a `&self` declaration in the trait, but not in the impl
2429
--> $DIR/trait_type.rs:17:4

tests/ui/suggestions/bad-infer-in-trait-impl.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ LL | fn bar();
1212
...
1313
LL | fn bar(s: _) {}
1414
| ^ expected 0 parameters, found 1
15+
|
16+
help: modify the signature to match the trait definition
17+
|
18+
LL - fn bar(s: _) {}
19+
LL + fn bar() {}
20+
|
1521

1622
error: aborting due to 2 previous errors
1723

tests/ui/traits/impl-different-num-params.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ LL | fn bar(&self, x: usize) -> Self;
66
...
77
LL | fn bar(&self) -> isize {
88
| ^^^^^ expected 2 parameters, found 1
9+
|
10+
help: modify the signature to match the trait definition
11+
|
12+
LL | fn bar(&self, x: usize) -> isize {
13+
| ++++++++++
914

1015
error: aborting due to 1 previous error
1116

tests/ui/traits/trait-method-signature-mismatch.stderr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ LL | fn foo(&mut self, x: i32, y: i32) -> i32;
77
LL | / &mut self,
88
LL | | x: i32,
99
| |______________^ expected 3 parameters, found 2
10+
|
11+
help: modify the signature to match the trait definition
12+
|
13+
LL - fn foo(
14+
LL - &mut self,
15+
LL - x: i32,
16+
LL - ) {
17+
LL + fn foo(&mut self, x: i32, y: i32) {
18+
|
1019

1120
error: aborting due to 1 previous error
1221

0 commit comments

Comments
 (0)