Skip to content

Commit 3d5d61f

Browse files
committed
Implement method signature suggestion for trait mismatches error
1 parent 5dbf406 commit 3d5d61f

File tree

5 files changed

+91
-1
lines changed

5 files changed

+91
-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;
@@ -1742,6 +1742,64 @@ fn compare_number_of_method_arguments<'tcx>(
17421742
),
17431743
);
17441744

1745+
let sm = tcx.sess.source_map();
1746+
let find_param_bounds = |snippet: &str| -> Option<(usize, usize)> {
1747+
let start = snippet.find('(')?;
1748+
let mut depth = 1usize;
1749+
let bytes = snippet.as_bytes();
1750+
let mut i = start + 1;
1751+
while i < bytes.len() {
1752+
match bytes[i] as char {
1753+
'(' => depth += 1,
1754+
')' => {
1755+
depth -= 1;
1756+
if depth == 0 {
1757+
return Some((start + 1, i));
1758+
}
1759+
}
1760+
_ => {}
1761+
}
1762+
i += 1;
1763+
}
1764+
None
1765+
};
1766+
1767+
let impl_inputs_span = (!impl_m_sig.span.is_dummy())
1768+
.then(|| {
1769+
sm.span_to_snippet(impl_m_sig.span).ok().and_then(|snippet| {
1770+
find_param_bounds(&snippet).map(|(lo_rel, hi_rel)| {
1771+
let lo = impl_m_sig.span.lo() + BytePos(lo_rel as u32);
1772+
let hi = impl_m_sig.span.lo() + BytePos(hi_rel as u32);
1773+
impl_m_sig.span.with_lo(lo).with_hi(hi)
1774+
})
1775+
})
1776+
})
1777+
.flatten();
1778+
1779+
let suggestion = trait_m
1780+
.def_id
1781+
.as_local()
1782+
.and_then(|def_id| {
1783+
let (trait_sig, _) = tcx.hir_expect_trait_item(def_id).expect_fn();
1784+
sm.span_to_snippet(trait_sig.span).ok().and_then(|snippet| {
1785+
find_param_bounds(&snippet)
1786+
.map(|(lo_rel, hi_rel)| snippet[lo_rel..hi_rel].to_string())
1787+
})
1788+
})
1789+
.or_else(|| {
1790+
let signature = trait_m.signature(tcx);
1791+
find_param_bounds(&signature).map(|(lo, hi)| signature[lo..hi].trim().to_string())
1792+
});
1793+
1794+
if let (Some(span), Some(suggestion)) = (impl_inputs_span, suggestion) {
1795+
err.span_suggestion_verbose(
1796+
span,
1797+
"modify the signature to match the trait definition",
1798+
suggestion,
1799+
Applicability::MaybeIncorrect,
1800+
);
1801+
}
1802+
17451803
return Err(err.emit_unless_delay(delay));
17461804
}
17471805

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/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/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

0 commit comments

Comments
 (0)