Skip to content

Commit a3c8572

Browse files
authored
Rollup merge of rust-lang#65752 - estebank:sugg, r=Centril
Use structured suggestions for missing associated items When encountering an `impl` that is missing associated items required by its `trait`, use structured suggestions at an appropriate place in the `impl`.
2 parents d7f1406 + a12a32a commit a3c8572

File tree

20 files changed

+243
-84
lines changed

20 files changed

+243
-84
lines changed

src/librustc/hir/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,13 @@ impl Mutability {
10751075
MutImmutable => MutMutable,
10761076
}
10771077
}
1078+
1079+
pub fn prefix_str(&self) -> &'static str {
1080+
match self {
1081+
MutMutable => "mut ",
1082+
MutImmutable => "",
1083+
}
1084+
}
10781085
}
10791086

10801087
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable)]
@@ -2184,6 +2191,15 @@ pub enum Unsafety {
21842191
Normal,
21852192
}
21862193

2194+
impl Unsafety {
2195+
pub fn prefix_str(&self) -> &'static str {
2196+
match self {
2197+
Unsafety::Unsafe => "unsafe ",
2198+
Unsafety::Normal => "",
2199+
}
2200+
}
2201+
}
2202+
21872203
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable)]
21882204
pub enum Constness {
21892205
Const,

src/librustc/hir/print.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,9 +1734,7 @@ impl<'a> State<'a> {
17341734
_ => false,
17351735
};
17361736
self.s.word("&");
1737-
if mutbl == hir::MutMutable {
1738-
self.s.word("mut ");
1739-
}
1737+
self.s.word(mutbl.prefix_str());
17401738
if is_range_inner {
17411739
self.popen();
17421740
}

src/librustc/infer/error_reporting/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -897,11 +897,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
897897
} else {
898898
r.push(' ');
899899
}
900-
s.push_highlighted(format!(
901-
"&{}{}",
902-
r,
903-
if mutbl == hir::MutMutable { "mut " } else { "" }
904-
));
900+
s.push_highlighted(format!("&{}{}", r, mutbl.prefix_str()));
905901
s.push_normal(ty.to_string());
906902
}
907903

src/librustc/ty/print/obsolete.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,7 @@ impl DefPathBasedNames<'tcx> {
8080
}
8181
ty::Ref(_, inner_type, mutbl) => {
8282
output.push('&');
83-
if mutbl == hir::MutMutable {
84-
output.push_str("mut ");
85-
}
83+
output.push_str(mutbl.prefix_str());
8684

8785
self.push_type_name(inner_type, output, debug);
8886
}
@@ -114,9 +112,7 @@ impl DefPathBasedNames<'tcx> {
114112
ty::Foreign(did) => self.push_def_path(did, output),
115113
ty::FnDef(..) | ty::FnPtr(_) => {
116114
let sig = t.fn_sig(self.tcx);
117-
if sig.unsafety() == hir::Unsafety::Unsafe {
118-
output.push_str("unsafe ");
119-
}
115+
output.push_str(sig.unsafety().prefix_str());
120116

121117
let abi = sig.abi();
122118
if abi != ::rustc_target::spec::abi::Abi::Rust {

src/librustc/ty/print/pretty.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,8 +1666,7 @@ define_print_and_forward_display! {
16661666
}
16671667

16681668
ty::TypeAndMut<'tcx> {
1669-
p!(write("{}", if self.mutbl == hir::MutMutable { "mut " } else { "" }),
1670-
print(self.ty))
1669+
p!(write("{}", self.mutbl.prefix_str()), print(self.ty))
16711670
}
16721671

16731672
ty::ExistentialTraitRef<'tcx> {
@@ -1693,9 +1692,7 @@ define_print_and_forward_display! {
16931692
}
16941693

16951694
ty::FnSig<'tcx> {
1696-
if self.unsafety == hir::Unsafety::Unsafe {
1697-
p!(write("unsafe "));
1698-
}
1695+
p!(write("{}", self.unsafety.prefix_str()));
16991696

17001697
if self.abi != Abi::Rust {
17011698
p!(write("extern {} ", self.abi));

src/librustc_codegen_ssa/debuginfo/type_names.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,7 @@ pub fn push_debuginfo_type_name<'tcx>(
7676
if !cpp_like_names {
7777
output.push('&');
7878
}
79-
if mutbl == hir::MutMutable {
80-
output.push_str("mut ");
81-
}
79+
output.push_str(mutbl.prefix_str());
8280

8381
push_debuginfo_type_name(tcx, inner_type, true, output, visited);
8482

@@ -140,9 +138,7 @@ pub fn push_debuginfo_type_name<'tcx>(
140138

141139

142140
let sig = t.fn_sig(tcx);
143-
if sig.unsafety() == hir::Unsafety::Unsafe {
144-
output.push_str("unsafe ");
145-
}
141+
output.push_str(sig.unsafety().prefix_str());
146142

147143
let abi = sig.abi();
148144
if abi != rustc_target::spec::abi::Abi::Rust {

src/librustc_mir/hair/pattern/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
293293
match self.ty.kind {
294294
ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
295295
ty::Ref(_, _, mutbl) => {
296-
write!(f, "&")?;
297-
if mutbl == hir::MutMutable {
298-
write!(f, "mut ")?;
299-
}
296+
write!(f, "&{}", mutbl.prefix_str())?;
300297
}
301298
_ => bug!("{} is a bad Deref pattern type", self.ty)
302299
}

src/librustc_typeck/check/cast.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
341341
tstr);
342342
match self.expr_ty.kind {
343343
ty::Ref(_, _, mt) => {
344-
let mtstr = match mt {
345-
hir::MutMutable => "mut ",
346-
hir::MutImmutable => "",
347-
};
344+
let mtstr = mt.prefix_str();
348345
if self.cast_ty.is_trait() {
349346
match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) {
350347
Ok(s) => {

src/librustc_typeck/check/expr.rs

Lines changed: 22 additions & 14 deletions
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+
pub(super) 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

Lines changed: 125 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ use syntax::ast;
127127
use syntax::attr;
128128
use syntax::feature_gate::{GateIssue, emit_feature_err};
129129
use syntax::source_map::{DUMMY_SP, original_sp};
130-
use syntax::symbol::{kw, sym};
130+
use syntax::symbol::{kw, sym, Ident};
131131
use syntax::util::parser::ExprPrecedence;
132132

133133
use std::cell::{Cell, RefCell, Ref, RefMut};
@@ -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`
@@ -1925,35 +1925,132 @@ fn check_impl_items_against_trait<'tcx>(
19251925
}
19261926

19271927
if !missing_items.is_empty() {
1928-
let mut err = struct_span_err!(tcx.sess, impl_span, E0046,
1929-
"not all trait items implemented, missing: `{}`",
1930-
missing_items.iter()
1931-
.map(|trait_item| trait_item.ident.to_string())
1932-
.collect::<Vec<_>>().join("`, `"));
1933-
err.span_label(impl_span, format!("missing `{}` in implementation",
1934-
missing_items.iter()
1935-
.map(|trait_item| trait_item.ident.to_string())
1936-
.collect::<Vec<_>>().join("`, `")));
1937-
for trait_item in missing_items {
1938-
if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
1939-
err.span_label(span, format!("`{}` from trait", trait_item.ident));
1940-
} else {
1941-
err.note_trait_signature(trait_item.ident.to_string(),
1942-
trait_item.signature(tcx));
1943-
}
1944-
}
1945-
err.emit();
1928+
missing_items_err(tcx, impl_span, &missing_items, full_impl_span);
19461929
}
19471930

19481931
if !invalidated_items.is_empty() {
19491932
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("`, `"))
1933+
span_err!(
1934+
tcx.sess,
1935+
invalidator.span,
1936+
E0399,
1937+
"the following trait items need to be reimplemented as `{}` was overridden: `{}`",
1938+
invalidator.ident,
1939+
invalidated_items.iter()
1940+
.map(|name| name.to_string())
1941+
.collect::<Vec<_>>().join("`, `")
1942+
)
1943+
}
1944+
}
1945+
1946+
fn missing_items_err(
1947+
tcx: TyCtxt<'_>,
1948+
impl_span: Span,
1949+
missing_items: &[ty::AssocItem],
1950+
full_impl_span: Span,
1951+
) {
1952+
let missing_items_msg = missing_items.iter()
1953+
.map(|trait_item| trait_item.ident.to_string())
1954+
.collect::<Vec<_>>().join("`, `");
1955+
1956+
let mut err = struct_span_err!(
1957+
tcx.sess,
1958+
impl_span,
1959+
E0046,
1960+
"not all trait items implemented, missing: `{}`",
1961+
missing_items_msg
1962+
);
1963+
err.span_label(impl_span, format!("missing `{}` in implementation", missing_items_msg));
1964+
1965+
// `Span` before impl block closing brace.
1966+
let hi = full_impl_span.hi() - BytePos(1);
1967+
// Point at the place right before the closing brace of the relevant `impl` to suggest
1968+
// adding the associated item at the end of its body.
1969+
let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
1970+
// Obtain the level of indentation ending in `sugg_sp`.
1971+
let indentation = tcx.sess.source_map().span_to_margin(sugg_sp).unwrap_or(0);
1972+
// Make the whitespace that will make the suggestion have the right indentation.
1973+
let padding: String = (0..indentation).map(|_| " ").collect();
1974+
1975+
for trait_item in missing_items {
1976+
let snippet = suggestion_signature(&trait_item, tcx);
1977+
let code = format!("{}{}\n{}", padding, snippet, padding);
1978+
let msg = format!("implement the missing item: `{}`", snippet);
1979+
let appl = Applicability::HasPlaceholders;
1980+
if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
1981+
err.span_label(span, format!("`{}` from trait", trait_item.ident));
1982+
err.tool_only_span_suggestion(sugg_sp, &msg, code, appl);
1983+
} else {
1984+
err.span_suggestion_hidden(sugg_sp, &msg, code, appl);
1985+
}
1986+
}
1987+
err.emit();
1988+
}
1989+
1990+
/// Return placeholder code for the given function.
1991+
fn fn_sig_suggestion(sig: &ty::FnSig<'_>, ident: Ident) -> String {
1992+
let args = sig.inputs()
1993+
.iter()
1994+
.map(|ty| Some(match ty.kind {
1995+
ty::Param(param) if param.name == kw::SelfUpper => "self".to_string(),
1996+
ty::Ref(reg, ref_ty, mutability) => {
1997+
let reg = match &format!("{}", reg)[..] {
1998+
"'_" | "" => String::new(),
1999+
reg => format!("{} ", reg),
2000+
};
2001+
match ref_ty.kind {
2002+
ty::Param(param) if param.name == kw::SelfUpper => {
2003+
format!("&{}{}self", reg, mutability.prefix_str())
2004+
}
2005+
_ => format!("_: {:?}", ty),
2006+
}
2007+
}
2008+
_ => format!("_: {:?}", ty),
2009+
}))
2010+
.chain(std::iter::once(if sig.c_variadic {
2011+
Some("...".to_string())
2012+
} else {
2013+
None
2014+
}))
2015+
.filter_map(|arg| arg)
2016+
.collect::<Vec<String>>()
2017+
.join(", ");
2018+
let output = sig.output();
2019+
let output = if !output.is_unit() {
2020+
format!(" -> {:?}", output)
2021+
} else {
2022+
String::new()
2023+
};
2024+
2025+
let unsafety = sig.unsafety.prefix_str();
2026+
// FIXME: this is not entirely correct, as the lifetimes from borrowed params will
2027+
// not be present in the `fn` definition, not will we account for renamed
2028+
// lifetimes between the `impl` and the `trait`, but this should be good enough to
2029+
// fill in a significant portion of the missing code, and other subsequent
2030+
// suggestions can help the user fix the code.
2031+
format!("{}fn {}({}){} {{ unimplemented!() }}", unsafety, ident, args, output)
2032+
}
2033+
2034+
/// Return placeholder code for the given associated item.
2035+
/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
2036+
/// structured suggestion.
2037+
fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
2038+
match assoc.kind {
2039+
ty::AssocKind::Method => {
2040+
// We skip the binder here because the binder would deanonymize all
2041+
// late-bound regions, and we don't want method signatures to show up
2042+
// `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
2043+
// regions just fine, showing `fn(&MyType)`.
2044+
fn_sig_suggestion(tcx.fn_sig(assoc.def_id).skip_binder(), assoc.ident)
2045+
}
2046+
ty::AssocKind::Type => format!("type {} = Type;", assoc.ident),
2047+
// FIXME(type_alias_impl_trait): we should print bounds here too.
2048+
ty::AssocKind::OpaqueTy => format!("type {} = Type;", assoc.ident),
2049+
ty::AssocKind::Const => {
2050+
let ty = tcx.type_of(assoc.def_id);
2051+
let val = expr::ty_kind_suggestion(ty).unwrap_or("value");
2052+
format!("const {}: {:?} = {};", assoc.ident, ty, val)
2053+
}
19572054
}
19582055
}
19592056

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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

0 commit comments

Comments
 (0)