diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 0edc41e6b4881..77713312ceca4 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1075,6 +1075,13 @@ impl Mutability { MutImmutable => MutMutable, } } + + pub fn prefix_str(&self) -> &'static str { + match self { + MutMutable => "mut ", + MutImmutable => "", + } + } } #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable)] @@ -2175,6 +2182,15 @@ pub enum Unsafety { Normal, } +impl Unsafety { + pub fn prefix_str(&self) -> &'static str { + match self { + Unsafety::Unsafe => "unsafe ", + Unsafety::Normal => "", + } + } +} + #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable)] pub enum Constness { Const, diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 328d475be0606..a25c111b59871 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1734,9 +1734,7 @@ impl<'a> State<'a> { _ => false, }; self.s.word("&"); - if mutbl == hir::MutMutable { - self.s.word("mut "); - } + self.s.word(mutbl.prefix_str()); if is_range_inner { self.popen(); } diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index e238c96612234..38edef50c9662 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -897,11 +897,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } else { r.push(' '); } - s.push_highlighted(format!( - "&{}{}", - r, - if mutbl == hir::MutMutable { "mut " } else { "" } - )); + s.push_highlighted(format!("&{}{}", r, mutbl.prefix_str())); s.push_normal(ty.to_string()); } diff --git a/src/librustc/ty/print/obsolete.rs b/src/librustc/ty/print/obsolete.rs index e72916de6a9c7..0b6060e0eb01c 100644 --- a/src/librustc/ty/print/obsolete.rs +++ b/src/librustc/ty/print/obsolete.rs @@ -80,9 +80,7 @@ impl DefPathBasedNames<'tcx> { } ty::Ref(_, inner_type, mutbl) => { output.push('&'); - if mutbl == hir::MutMutable { - output.push_str("mut "); - } + output.push_str(mutbl.prefix_str()); self.push_type_name(inner_type, output, debug); } @@ -114,9 +112,7 @@ impl DefPathBasedNames<'tcx> { ty::Foreign(did) => self.push_def_path(did, output), ty::FnDef(..) | ty::FnPtr(_) => { let sig = t.fn_sig(self.tcx); - if sig.unsafety() == hir::Unsafety::Unsafe { - output.push_str("unsafe "); - } + output.push_str(sig.unsafety().prefix_str()); let abi = sig.abi(); if abi != ::rustc_target::spec::abi::Abi::Rust { diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs index 8a98a5d83615f..c4785621985c3 100644 --- a/src/librustc/ty/print/pretty.rs +++ b/src/librustc/ty/print/pretty.rs @@ -1666,8 +1666,7 @@ define_print_and_forward_display! { } ty::TypeAndMut<'tcx> { - p!(write("{}", if self.mutbl == hir::MutMutable { "mut " } else { "" }), - print(self.ty)) + p!(write("{}", self.mutbl.prefix_str()), print(self.ty)) } ty::ExistentialTraitRef<'tcx> { @@ -1693,9 +1692,7 @@ define_print_and_forward_display! { } ty::FnSig<'tcx> { - if self.unsafety == hir::Unsafety::Unsafe { - p!(write("unsafe ")); - } + p!(write("{}", self.unsafety.prefix_str())); if self.abi != Abi::Rust { p!(write("extern {} ", self.abi)); diff --git a/src/librustc_codegen_ssa/debuginfo/type_names.rs b/src/librustc_codegen_ssa/debuginfo/type_names.rs index 166a74fe48795..b9fa53d638fcb 100644 --- a/src/librustc_codegen_ssa/debuginfo/type_names.rs +++ b/src/librustc_codegen_ssa/debuginfo/type_names.rs @@ -76,9 +76,7 @@ pub fn push_debuginfo_type_name<'tcx>( if !cpp_like_names { output.push('&'); } - if mutbl == hir::MutMutable { - output.push_str("mut "); - } + output.push_str(mutbl.prefix_str()); push_debuginfo_type_name(tcx, inner_type, true, output, visited); @@ -140,9 +138,7 @@ pub fn push_debuginfo_type_name<'tcx>( let sig = t.fn_sig(tcx); - if sig.unsafety() == hir::Unsafety::Unsafe { - output.push_str("unsafe "); - } + output.push_str(sig.unsafety().prefix_str()); let abi = sig.abi(); if abi != rustc_target::spec::abi::Abi::Rust { diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 1ecc78ba227ce..477ad10460f6b 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -293,10 +293,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { match self.ty.kind { ty::Adt(def, _) if def.is_box() => write!(f, "box ")?, ty::Ref(_, _, mutbl) => { - write!(f, "&")?; - if mutbl == hir::MutMutable { - write!(f, "mut ")?; - } + write!(f, "&{}", mutbl.prefix_str())?; } _ => bug!("{} is a bad Deref pattern type", self.ty) } diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 9cbde276ae97c..ded655c1ae32a 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -341,10 +341,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { tstr); match self.expr_ty.kind { ty::Ref(_, _, mt) => { - let mtstr = match mt { - hir::MutMutable => "mut ", - hir::MutImmutable => "", - }; + let mtstr = mt.prefix_str(); if self.cast_ty.is_trait() { match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { Ok(s) => { diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 35870abbaefbd..bc1189e443e28 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -592,20 +592,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { cause.span, target_id, ); - let val = match ty.kind { - ty::Bool => "true", - ty::Char => "'a'", - ty::Int(_) | ty::Uint(_) => "42", - ty::Float(_) => "3.14159", - ty::Error | ty::Never => return, - _ => "value", - }; - let msg = "give it a value of the expected type"; - let label = destination.label - .map(|l| format!(" {}", l.ident)) - .unwrap_or_else(String::new); - let sugg = format!("break{} {}", label, val); - err.span_suggestion(expr.span, msg, sugg, Applicability::HasPlaceholders); + if let Some(val) = ty_kind_suggestion(ty) { + let label = destination.label + .map(|l| format!(" {}", l.ident)) + .unwrap_or_else(String::new); + err.span_suggestion( + expr.span, + "give it a value of the expected type", + format!("break{} {}", label, val), + Applicability::HasPlaceholders, + ); + } }, false); } } else { @@ -1725,3 +1722,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_unit() } } + +pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { + Some(match ty.kind { + ty::Bool => "true", + ty::Char => "'a'", + ty::Int(_) | ty::Uint(_) => "42", + ty::Float(_) => "3.14159", + ty::Error | ty::Never => return None, + _ => "value", + }) +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 26f040810462f..845fc231429c8 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -127,7 +127,7 @@ use syntax::ast; use syntax::attr; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::source_map::{DUMMY_SP, original_sp}; -use syntax::symbol::{kw, sym}; +use syntax::symbol::{kw, sym, Ident}; use syntax::util::parser::ExprPrecedence; use std::cell::{Cell, RefCell, Ref, RefMut}; @@ -1800,12 +1800,12 @@ fn check_specialization_validity<'tcx>( fn check_impl_items_against_trait<'tcx>( tcx: TyCtxt<'tcx>, - impl_span: Span, + full_impl_span: Span, impl_id: DefId, impl_trait_ref: ty::TraitRef<'tcx>, impl_item_refs: &[hir::ImplItemRef], ) { - let impl_span = tcx.sess.source_map().def_span(impl_span); + let impl_span = tcx.sess.source_map().def_span(full_impl_span); // If the trait reference itself is erroneous (so the compilation is going // to fail), skip checking the items here -- the `impl_item` table in `tcx` @@ -1925,35 +1925,132 @@ fn check_impl_items_against_trait<'tcx>( } if !missing_items.is_empty() { - let mut err = struct_span_err!(tcx.sess, impl_span, E0046, - "not all trait items implemented, missing: `{}`", - missing_items.iter() - .map(|trait_item| trait_item.ident.to_string()) - .collect::>().join("`, `")); - err.span_label(impl_span, format!("missing `{}` in implementation", - missing_items.iter() - .map(|trait_item| trait_item.ident.to_string()) - .collect::>().join("`, `"))); - for trait_item in missing_items { - if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { - err.span_label(span, format!("`{}` from trait", trait_item.ident)); - } else { - err.note_trait_signature(trait_item.ident.to_string(), - trait_item.signature(tcx)); - } - } - err.emit(); + missing_items_err(tcx, impl_span, &missing_items, full_impl_span); } if !invalidated_items.is_empty() { let invalidator = overridden_associated_type.unwrap(); - span_err!(tcx.sess, invalidator.span, E0399, - "the following trait items need to be reimplemented \ - as `{}` was overridden: `{}`", - invalidator.ident, - invalidated_items.iter() - .map(|name| name.to_string()) - .collect::>().join("`, `")) + span_err!( + tcx.sess, + invalidator.span, + E0399, + "the following trait items need to be reimplemented as `{}` was overridden: `{}`", + invalidator.ident, + invalidated_items.iter() + .map(|name| name.to_string()) + .collect::>().join("`, `") + ) + } +} + +fn missing_items_err( + tcx: TyCtxt<'_>, + impl_span: Span, + missing_items: &[ty::AssocItem], + full_impl_span: Span, +) { + let missing_items_msg = missing_items.iter() + .map(|trait_item| trait_item.ident.to_string()) + .collect::>().join("`, `"); + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0046, + "not all trait items implemented, missing: `{}`", + missing_items_msg + ); + err.span_label(impl_span, format!("missing `{}` in implementation", missing_items_msg)); + + // `Span` before impl block closing brace. + let hi = full_impl_span.hi() - BytePos(1); + // Point at the place right before the closing brace of the relevant `impl` to suggest + // adding the associated item at the end of its body. + let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi); + // Obtain the level of indentation ending in `sugg_sp`. + let indentation = tcx.sess.source_map().span_to_margin(sugg_sp).unwrap_or(0); + // Make the whitespace that will make the suggestion have the right indentation. + let padding: String = (0..indentation).map(|_| " ").collect(); + + for trait_item in missing_items { + let snippet = suggestion_signature(&trait_item, tcx); + let code = format!("{}{}\n{}", padding, snippet, padding); + let msg = format!("implement the missing item: `{}`", snippet); + let appl = Applicability::HasPlaceholders; + if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { + err.span_label(span, format!("`{}` from trait", trait_item.ident)); + err.tool_only_span_suggestion(sugg_sp, &msg, code, appl); + } else { + err.span_suggestion_hidden(sugg_sp, &msg, code, appl); + } + } + err.emit(); +} + +/// Return placeholder code for the given function. +fn fn_sig_suggestion(sig: &ty::FnSig<'_>, ident: Ident) -> String { + let args = sig.inputs() + .iter() + .map(|ty| Some(match ty.kind { + ty::Param(param) if param.name == kw::SelfUpper => "self".to_string(), + ty::Ref(reg, ref_ty, mutability) => { + let reg = match &format!("{}", reg)[..] { + "'_" | "" => String::new(), + reg => format!("{} ", reg), + }; + match ref_ty.kind { + ty::Param(param) if param.name == kw::SelfUpper => { + format!("&{}{}self", reg, mutability.prefix_str()) + } + _ => format!("_: {:?}", ty), + } + } + _ => format!("_: {:?}", ty), + })) + .chain(std::iter::once(if sig.c_variadic { + Some("...".to_string()) + } else { + None + })) + .filter_map(|arg| arg) + .collect::>() + .join(", "); + let output = sig.output(); + let output = if !output.is_unit() { + format!(" -> {:?}", output) + } else { + String::new() + }; + + let unsafety = sig.unsafety.prefix_str(); + // FIXME: this is not entirely correct, as the lifetimes from borrowed params will + // not be present in the `fn` definition, not will we account for renamed + // lifetimes between the `impl` and the `trait`, but this should be good enough to + // fill in a significant portion of the missing code, and other subsequent + // suggestions can help the user fix the code. + format!("{}fn {}({}){} {{ unimplemented!() }}", unsafety, ident, args, output) +} + +/// Return placeholder code for the given associated item. +/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a +/// structured suggestion. +fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { + match assoc.kind { + ty::AssocKind::Method => { + // We skip the binder here because the binder would deanonymize all + // late-bound regions, and we don't want method signatures to show up + // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound + // regions just fine, showing `fn(&MyType)`. + fn_sig_suggestion(tcx.fn_sig(assoc.def_id).skip_binder(), assoc.ident) + } + ty::AssocKind::Type => format!("type {} = Type;", assoc.ident), + // FIXME(type_alias_impl_trait): we should print bounds here too. + ty::AssocKind::OpaqueTy => format!("type {} = Type;", assoc.ident), + ty::AssocKind::Const => { + let ty = tcx.type_of(assoc.def_id); + let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); + format!("const {}: {:?} = {};", assoc.ident, ty, val) + } } } diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr index 129d7ef5783ce..151dc68162155 100644 --- a/src/test/ui/impl-trait/trait_type.stderr +++ b/src/test/ui/impl-trait/trait_type.stderr @@ -29,7 +29,7 @@ error[E0046]: not all trait items implemented, missing: `fmt` LL | impl std::fmt::Display for MyType4 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation | - = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` + = help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { unimplemented!() }` error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-3344.stderr b/src/test/ui/issues/issue-3344.stderr index 6593e07b18973..271fbb6c87426 100644 --- a/src/test/ui/issues/issue-3344.stderr +++ b/src/test/ui/issues/issue-3344.stderr @@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `partial_cmp` LL | impl PartialOrd for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `partial_cmp` in implementation | - = note: `partial_cmp` from trait: `fn(&Self, &Rhs) -> std::option::Option` + = help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> std::option::Option { unimplemented!() }` error: aborting due to previous error diff --git a/src/test/ui/missing/missing-items/m2.stderr b/src/test/ui/missing/missing-items/m2.stderr index d2dac4ca6454a..f8243528d72cf 100644 --- a/src/test/ui/missing/missing-items/m2.stderr +++ b/src/test/ui/missing/missing-items/m2.stderr @@ -4,9 +4,9 @@ error[E0046]: not all trait items implemented, missing: `CONSTANT`, `Type`, `met LL | impl m1::X for X { | ^^^^^^^^^^^^^^^^ missing `CONSTANT`, `Type`, `method` in implementation | - = note: `CONSTANT` from trait: `const CONSTANT: u32;` - = note: `Type` from trait: `type Type;` - = note: `method` from trait: `fn(&Self, std::string::String) -> ::Type` + = help: implement the missing item: `const CONSTANT: u32 = 42;` + = help: implement the missing item: `type Type = Type;` + = help: implement the missing item: `fn method(&self, _: std::string::String) -> ::Type { unimplemented!() }` error: aborting due to previous error diff --git a/src/test/ui/span/impl-wrong-item-for-trait.stderr b/src/test/ui/span/impl-wrong-item-for-trait.stderr index 81409aac2897d..f23f421edc7c5 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.stderr +++ b/src/test/ui/span/impl-wrong-item-for-trait.stderr @@ -64,7 +64,7 @@ error[E0046]: not all trait items implemented, missing: `fmt` LL | impl Debug for FooTypeForMethod { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation | - = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` + = help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { unimplemented!() }` error: aborting due to 8 previous errors diff --git a/src/test/ui/span/issue-23729.stderr b/src/test/ui/span/issue-23729.stderr index 865fae917c5db..f88ce6c88db23 100644 --- a/src/test/ui/span/issue-23729.stderr +++ b/src/test/ui/span/issue-23729.stderr @@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `Item` LL | impl Iterator for Recurrence { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `Item` in implementation | - = note: `Item` from trait: `type Item;` + = help: implement the missing item: `type Item = Type;` error: aborting due to previous error diff --git a/src/test/ui/span/issue-23827.stderr b/src/test/ui/span/issue-23827.stderr index a8e3e9b6b9ae9..46a820f1b7660 100644 --- a/src/test/ui/span/issue-23827.stderr +++ b/src/test/ui/span/issue-23827.stderr @@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `Output` LL | impl FnOnce<(C,)> for Prototype { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `Output` in implementation | - = note: `Output` from trait: `type Output;` + = help: implement the missing item: `type Output = Type;` error: aborting due to previous error diff --git a/src/test/ui/span/issue-24356.stderr b/src/test/ui/span/issue-24356.stderr index 4827e9ddd50fd..a1f9b25502019 100644 --- a/src/test/ui/span/issue-24356.stderr +++ b/src/test/ui/span/issue-24356.stderr @@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `Target` LL | impl Deref for Thing { | ^^^^^^^^^^^^^^^^^^^^ missing `Target` in implementation | - = note: `Target` from trait: `type Target;` + = help: implement the missing item: `type Target = Type;` error: aborting due to previous error diff --git a/src/test/ui/suggestions/missing-trait-item.fixed b/src/test/ui/suggestions/missing-trait-item.fixed new file mode 100644 index 0000000000000..42f579a665e5f --- /dev/null +++ b/src/test/ui/suggestions/missing-trait-item.fixed @@ -0,0 +1,20 @@ +// run-rustfix + +trait T { + unsafe fn foo(a: &usize, b: &usize) -> usize; + fn bar(&self, a: &usize, b: &usize) -> usize; +} + +mod foo { + use super::T; + impl T for () { fn bar(&self, _: &usize, _: &usize) -> usize { unimplemented!() } + unsafe fn foo(_: &usize, _: &usize) -> usize { unimplemented!() } + } //~ ERROR not all trait items + + impl T for usize { //~ ERROR not all trait items + fn bar(&self, _: &usize, _: &usize) -> usize { unimplemented!() } + unsafe fn foo(_: &usize, _: &usize) -> usize { unimplemented!() } + } +} + +fn main() {} diff --git a/src/test/ui/suggestions/missing-trait-item.rs b/src/test/ui/suggestions/missing-trait-item.rs new file mode 100644 index 0000000000000..b4fca25ba2f11 --- /dev/null +++ b/src/test/ui/suggestions/missing-trait-item.rs @@ -0,0 +1,16 @@ +// run-rustfix + +trait T { + unsafe fn foo(a: &usize, b: &usize) -> usize; + fn bar(&self, a: &usize, b: &usize) -> usize; +} + +mod foo { + use super::T; + impl T for () {} //~ ERROR not all trait items + + impl T for usize { //~ ERROR not all trait items + } +} + +fn main() {} diff --git a/src/test/ui/suggestions/missing-trait-item.stderr b/src/test/ui/suggestions/missing-trait-item.stderr new file mode 100644 index 0000000000000..4a9d7b472c93a --- /dev/null +++ b/src/test/ui/suggestions/missing-trait-item.stderr @@ -0,0 +1,25 @@ +error[E0046]: not all trait items implemented, missing: `foo`, `bar` + --> $DIR/missing-trait-item.rs:10:5 + | +LL | unsafe fn foo(a: &usize, b: &usize) -> usize; + | --------------------------------------------- `foo` from trait +LL | fn bar(&self, a: &usize, b: &usize) -> usize; + | --------------------------------------------- `bar` from trait +... +LL | impl T for () {} + | ^^^^^^^^^^^^^ missing `foo`, `bar` in implementation + +error[E0046]: not all trait items implemented, missing: `foo`, `bar` + --> $DIR/missing-trait-item.rs:12:5 + | +LL | unsafe fn foo(a: &usize, b: &usize) -> usize; + | --------------------------------------------- `foo` from trait +LL | fn bar(&self, a: &usize, b: &usize) -> usize; + | --------------------------------------------- `bar` from trait +... +LL | impl T for usize { + | ^^^^^^^^^^^^^^^^ missing `foo`, `bar` in implementation + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0046`.