Skip to content

Commit f545a50

Browse files
committed
Suggest missing item from trait in impl
1 parent 3f0e164 commit f545a50

File tree

12 files changed

+192
-34
lines changed

12 files changed

+192
-34
lines changed

src/librustc_typeck/check/expr.rs

+22-14
Original file line numberDiff line numberDiff line change
@@ -592,20 +592,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
592592
cause.span,
593593
target_id,
594594
);
595-
let val = match ty.kind {
596-
ty::Bool => "true",
597-
ty::Char => "'a'",
598-
ty::Int(_) | ty::Uint(_) => "42",
599-
ty::Float(_) => "3.14159",
600-
ty::Error | ty::Never => return,
601-
_ => "value",
602-
};
603-
let msg = "give it a value of the expected type";
604-
let label = destination.label
605-
.map(|l| format!(" {}", l.ident))
606-
.unwrap_or_else(String::new);
607-
let sugg = format!("break{} {}", label, val);
608-
err.span_suggestion(expr.span, msg, sugg, Applicability::HasPlaceholders);
595+
if let Some(val) = ty_kind_suggestion(ty) {
596+
let label = destination.label
597+
.map(|l| format!(" {}", l.ident))
598+
.unwrap_or_else(String::new);
599+
err.span_suggestion(
600+
expr.span,
601+
"give it a value of the expected type",
602+
format!("break{} {}", label, val),
603+
Applicability::HasPlaceholders,
604+
);
605+
}
609606
}, false);
610607
}
611608
} else {
@@ -1725,3 +1722,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17251722
self.tcx.mk_unit()
17261723
}
17271724
}
1725+
1726+
crate fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
1727+
Some(match ty.kind {
1728+
ty::Bool => "true",
1729+
ty::Char => "'a'",
1730+
ty::Int(_) | ty::Uint(_) => "42",
1731+
ty::Float(_) => "3.14159",
1732+
ty::Error | ty::Never => return None,
1733+
_ => "value",
1734+
})
1735+
}

src/librustc_typeck/check/mod.rs

+100-11
Original file line numberDiff line numberDiff line change
@@ -1800,12 +1800,12 @@ fn check_specialization_validity<'tcx>(
18001800

18011801
fn check_impl_items_against_trait<'tcx>(
18021802
tcx: TyCtxt<'tcx>,
1803-
impl_span: Span,
1803+
full_impl_span: Span,
18041804
impl_id: DefId,
18051805
impl_trait_ref: ty::TraitRef<'tcx>,
18061806
impl_item_refs: &[hir::ImplItemRef],
18071807
) {
1808-
let impl_span = tcx.sess.source_map().def_span(impl_span);
1808+
let impl_span = tcx.sess.source_map().def_span(full_impl_span);
18091809

18101810
// If the trait reference itself is erroneous (so the compilation is going
18111811
// to fail), skip checking the items here -- the `impl_item` table in `tcx`
@@ -1934,26 +1934,115 @@ fn check_impl_items_against_trait<'tcx>(
19341934
missing_items.iter()
19351935
.map(|trait_item| trait_item.ident.to_string())
19361936
.collect::<Vec<_>>().join("`, `")));
1937+
1938+
// `Span` before impl block closing brace.
1939+
let hi = full_impl_span.hi() - BytePos(1);
1940+
let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
1941+
let indentation = tcx.sess.source_map().span_to_margin(sugg_sp).unwrap_or(0);
1942+
let padding: String = (0..indentation).map(|_| " ").collect();
19371943
for trait_item in missing_items {
1944+
let snippet = suggestion_signature(&trait_item, tcx);
1945+
let code = format!("{}{}\n{}", padding, snippet, padding);
1946+
let msg = format!("implement the missing item: `{}`", snippet);
1947+
let appl = Applicability::HasPlaceholders;
19381948
if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
19391949
err.span_label(span, format!("`{}` from trait", trait_item.ident));
1950+
err.tool_only_span_suggestion(sugg_sp, &msg, code, appl);
19401951
} else {
1941-
err.note_trait_signature(trait_item.ident.to_string(),
1942-
trait_item.signature(tcx));
1952+
err.span_suggestion_hidden(sugg_sp, &msg, code, appl);
19431953
}
19441954
}
19451955
err.emit();
19461956
}
19471957

19481958
if !invalidated_items.is_empty() {
19491959
let invalidator = overridden_associated_type.unwrap();
1950-
span_err!(tcx.sess, invalidator.span, E0399,
1951-
"the following trait items need to be reimplemented \
1952-
as `{}` was overridden: `{}`",
1953-
invalidator.ident,
1954-
invalidated_items.iter()
1955-
.map(|name| name.to_string())
1956-
.collect::<Vec<_>>().join("`, `"))
1960+
span_err!(
1961+
tcx.sess,
1962+
invalidator.span,
1963+
E0399,
1964+
"the following trait items need to be reimplemented as `{}` was overridden: `{}`",
1965+
invalidator.ident,
1966+
invalidated_items.iter()
1967+
.map(|name| name.to_string())
1968+
.collect::<Vec<_>>().join("`, `"))
1969+
}
1970+
}
1971+
1972+
/// Given a `ty::AssocItem` and a `TyCtxt`, return placeholder code for that associated item.
1973+
/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
1974+
/// structured suggestion.
1975+
fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
1976+
match assoc.kind {
1977+
ty::AssocKind::Method => {
1978+
// We skip the binder here because the binder would deanonymize all
1979+
// late-bound regions, and we don't want method signatures to show up
1980+
// `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
1981+
// regions just fine, showing `fn(&MyType)`.
1982+
let sig = tcx.fn_sig(assoc.def_id);
1983+
let unsafety = match sig.unsafety() {
1984+
hir::Unsafety::Unsafe => "unsafe ",
1985+
_ => "",
1986+
};
1987+
let args = sig.inputs()
1988+
.skip_binder()
1989+
.iter()
1990+
.map(|ty| Some(match ty.kind {
1991+
ty::Param(param) if param.name == kw::SelfUpper => {
1992+
"self".to_string()
1993+
}
1994+
ty::Ref(reg, ref_ty, mutability) => {
1995+
let mutability = match mutability {
1996+
hir::Mutability::MutMutable => "mut ",
1997+
_ => "",
1998+
};
1999+
let mut reg = format!("{}", reg);
2000+
if &reg[..] == "'_" {
2001+
reg = "".to_string();
2002+
}
2003+
if &reg[..] != "" {
2004+
reg = format!("{} ", reg);
2005+
}
2006+
match ref_ty.kind {
2007+
ty::Param(param)
2008+
if param.name == kw::SelfUpper => {
2009+
format!("&{}{}self", reg, mutability)
2010+
}
2011+
_ => format!("_: {:?}", ty),
2012+
}
2013+
2014+
}
2015+
_ => format!("_: {:?}", ty),
2016+
}))
2017+
.chain(std::iter::once(if sig.c_variadic() {
2018+
Some("...".to_string())
2019+
} else {
2020+
None
2021+
}))
2022+
.filter_map(|arg| arg)
2023+
.collect::<Vec<String>>()
2024+
.join(", ");
2025+
let output = sig.output();
2026+
let output = if !output.skip_binder().is_unit() {
2027+
format!(" -> {:?}", output.skip_binder())
2028+
} else {
2029+
String::new()
2030+
};
2031+
// FIXME: this is not entirely correct, as the lifetimes from borrowed params will
2032+
// not be present in the `fn` definition, not will we account for renamed
2033+
// lifetimes between the `impl` and the `trait`, but this should be good enough to
2034+
// fill in a significant portion of the missing code, and other subsequent
2035+
// suggestions can help the user fix the code.
2036+
format!("{}fn {}({}){} {{ unimplemented!() }}", unsafety, assoc.ident, args, output)
2037+
}
2038+
ty::AssocKind::Type => format!("type {} = Type;", assoc.ident),
2039+
// FIXME(type_alias_impl_trait): we should print bounds here too.
2040+
ty::AssocKind::OpaqueTy => format!("type {} = Type;", assoc.ident),
2041+
ty::AssocKind::Const => {
2042+
let ty = tcx.type_of(assoc.def_id);
2043+
let val = expr::ty_kind_suggestion(ty).unwrap_or("value");
2044+
format!("const {}: {:?} = {};", assoc.ident, ty, val)
2045+
}
19572046
}
19582047
}
19592048

src/test/ui/impl-trait/trait_type.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ error[E0046]: not all trait items implemented, missing: `fmt`
2929
LL | impl std::fmt::Display for MyType4 {}
3030
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation
3131
|
32-
= note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>`
32+
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { unimplemented!() }`
3333

3434
error: aborting due to 4 previous errors
3535

src/test/ui/issues/issue-3344.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `partial_cmp`
44
LL | impl PartialOrd for Thing {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `partial_cmp` in implementation
66
|
7-
= note: `partial_cmp` from trait: `fn(&Self, &Rhs) -> std::option::Option<std::cmp::Ordering>`
7+
= help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> std::option::Option<std::cmp::Ordering> { unimplemented!() }`
88

99
error: aborting due to previous error
1010

src/test/ui/missing/missing-items/m2.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ error[E0046]: not all trait items implemented, missing: `CONSTANT`, `Type`, `met
44
LL | impl m1::X for X {
55
| ^^^^^^^^^^^^^^^^ missing `CONSTANT`, `Type`, `method` in implementation
66
|
7-
= note: `CONSTANT` from trait: `const CONSTANT: u32;`
8-
= note: `Type` from trait: `type Type;`
9-
= note: `method` from trait: `fn(&Self, std::string::String) -> <Self as m1::X>::Type`
7+
= help: implement the missing item: `const CONSTANT: u32 = 42;`
8+
= help: implement the missing item: `type Type = Type;`
9+
= help: implement the missing item: `fn method(&self, _: std::string::String) -> <Self as m1::X>::Type { unimplemented!() }`
1010

1111
error: aborting due to previous error
1212

src/test/ui/span/impl-wrong-item-for-trait.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ error[E0046]: not all trait items implemented, missing: `fmt`
6464
LL | impl Debug for FooTypeForMethod {
6565
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation
6666
|
67-
= note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>`
67+
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { unimplemented!() }`
6868

6969
error: aborting due to 8 previous errors
7070

src/test/ui/span/issue-23729.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `Item`
44
LL | impl Iterator for Recurrence {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `Item` in implementation
66
|
7-
= note: `Item` from trait: `type Item;`
7+
= help: implement the missing item: `type Item = Type;`
88

99
error: aborting due to previous error
1010

src/test/ui/span/issue-23827.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `Output`
44
LL | impl<C: Component> FnOnce<(C,)> for Prototype {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `Output` in implementation
66
|
7-
= note: `Output` from trait: `type Output;`
7+
= help: implement the missing item: `type Output = Type;`
88

99
error: aborting due to previous error
1010

src/test/ui/span/issue-24356.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `Target`
44
LL | impl Deref for Thing {
55
| ^^^^^^^^^^^^^^^^^^^^ missing `Target` in implementation
66
|
7-
= note: `Target` from trait: `type Target;`
7+
= help: implement the missing item: `type Target = Type;`
88

99
error: aborting due to previous error
1010

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// run-rustfix
2+
3+
trait T {
4+
unsafe fn foo(a: &usize, b: &usize) -> usize;
5+
fn bar(&self, a: &usize, b: &usize) -> usize;
6+
}
7+
8+
mod foo {
9+
use super::T;
10+
impl T for () { fn bar(&self, _: &usize, _: &usize) -> usize { unimplemented!() }
11+
unsafe fn foo(_: &usize, _: &usize) -> usize { unimplemented!() }
12+
} //~ ERROR not all trait items
13+
14+
impl T for usize { //~ ERROR not all trait items
15+
fn bar(&self, _: &usize, _: &usize) -> usize { unimplemented!() }
16+
unsafe fn foo(_: &usize, _: &usize) -> usize { unimplemented!() }
17+
}
18+
}
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
3+
trait T {
4+
unsafe fn foo(a: &usize, b: &usize) -> usize;
5+
fn bar(&self, a: &usize, b: &usize) -> usize;
6+
}
7+
8+
mod foo {
9+
use super::T;
10+
impl T for () {} //~ ERROR not all trait items
11+
12+
impl T for usize { //~ ERROR not all trait items
13+
}
14+
}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0046]: not all trait items implemented, missing: `foo`, `bar`
2+
--> $DIR/missing-trait-item.rs:10:5
3+
|
4+
LL | unsafe fn foo(a: &usize, b: &usize) -> usize;
5+
| --------------------------------------------- `foo` from trait
6+
LL | fn bar(&self, a: &usize, b: &usize) -> usize;
7+
| --------------------------------------------- `bar` from trait
8+
...
9+
LL | impl T for () {}
10+
| ^^^^^^^^^^^^^ missing `foo`, `bar` in implementation
11+
12+
error[E0046]: not all trait items implemented, missing: `foo`, `bar`
13+
--> $DIR/missing-trait-item.rs:12:5
14+
|
15+
LL | unsafe fn foo(a: &usize, b: &usize) -> usize;
16+
| --------------------------------------------- `foo` from trait
17+
LL | fn bar(&self, a: &usize, b: &usize) -> usize;
18+
| --------------------------------------------- `bar` from trait
19+
...
20+
LL | impl T for usize {
21+
| ^^^^^^^^^^^^^^^^ missing `foo`, `bar` in implementation
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0046`.

0 commit comments

Comments
 (0)