diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 3ffc95e64f5a7..9ec1685163ef5 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -62,6 +62,27 @@ pub enum Node<'ast> { NodeTyParam(&'ast TyParam) } +impl<'ast> Node<'ast> { + pub fn span(&self) -> Option { + match *self { + NodeItem(item) => Some(item.span), + NodeForeignItem(item) => Some(item.span), + NodeTraitItem(item) => Some(item.span), + NodeImplItem(item) => Some(item.span), + NodeVariant(_) => None, + NodeExpr(item) => Some(item.span), + NodeStmt(_) => None, + NodeTy(ty) => Some(ty.span), + NodeLocal(pat) => Some(pat.span), + NodePat(pat) => Some(pat.span), + NodeBlock(block) => Some(block.span), + NodeStructCtor(_) => None, + NodeLifetime(lifetime) => Some(lifetime.span), + NodeTyParam(ty) => Some(ty.span), + } + } +} + /// Represents an entry and its parent NodeID. /// The odd layout is to bring down the total size. #[derive(Copy, Debug)] diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index e88f72f2d84bc..9658118b29643 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -249,6 +249,40 @@ impl<'tcx> ImplOrTraitItem<'tcx> { _ => None, } } + + pub fn signature<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> String { + match *self { + MethodTraitItem(ref item) => { + match tcx.map.get_if_local(item.def_id) { + Some(node) => { + match node.span() { + Some(span) => match tcx.sess.codemap().span_to_oneline_snippet(span) { + Ok(snippet) => snippet, + Err(_) => item.signature(), + }, + None => item.signature(), + } + } + None => item.signature(), + } + } + TypeTraitItem(ref item) => item.signature(), + ConstTraitItem(ref item) => { + match tcx.map.get_if_local(item.def_id) { + Some(node) => { + match node.span() { + Some(span) => match tcx.sess.codemap().span_to_oneline_snippet(span) { + Ok(snippet) => snippet, + Err(_) => item.signature(), + }, + None => item.signature(), + } + } + None => item.signature(), + } + } + } + } } #[derive(Clone, Copy, Debug)] @@ -380,6 +414,34 @@ impl<'tcx> Method<'tcx> { ImplContainer(id) => id, } } + + pub fn signature(&self) -> String { + let name = self.name.to_string(); + let unsafety = match self.fty.unsafety { + hir::Unsafety::Unsafe => "unsafe ", + hir::Unsafety::Normal => "", + }; + let has_gen_types = !self.generics.types.is_empty(); + let type_args = if has_gen_types { + format!("<{}>", self.generics.types.clone().into_iter() + .map(|t| t.name.as_str().to_string()) + .collect::>() + .join(", ")) + } else { + String::new() + }; + let args = self.fty.sig.inputs().0.iter() + .map(|t| format!("{:?}", t)).collect::>().join(", "); + let return_type = format!("{:?}", self.fty.sig.output().0); + let return_signature = if &return_type == "()" { + "".to_string() + } else { + format!(" -> {}", return_type) + }; + + // unsafe fn name<'a, T>(args) -> ReturnType + format!("{}fn {}{}({}){};", unsafety, name, type_args, args, return_signature) + } } impl<'tcx> PartialEq for Method<'tcx> { @@ -407,6 +469,18 @@ pub struct AssociatedConst<'tcx> { pub has_value: bool } +impl<'tcx> AssociatedConst<'tcx> { + pub fn signature(&self) -> String { + // const FOO: Type = DEFAULT; + let value = if self.has_value { + " = " + } else { + "" + }; + format!("const {}: {:?}{};", self.name.to_string(), self.ty, value) + } +} + #[derive(Clone, Copy, Debug)] pub struct AssociatedType<'tcx> { pub name: Name, @@ -417,6 +491,13 @@ pub struct AssociatedType<'tcx> { pub container: ImplOrTraitItemContainer, } +impl<'tcx> AssociatedType<'tcx> { + pub fn signature(&self) -> String { + //// type Type; + format!("type {};", self.name.to_string()) + } +} + #[derive(Clone, PartialEq, RustcDecodable, RustcEncodable, Copy)] pub enum Variance { Covariant, // T <: T iff A <: B -- e.g., function return type diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f4fea5542b3de..21fc7dccdbaad 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1106,7 +1106,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, if !is_implemented { if !is_provided { - missing_items.push(trait_item.name()); + missing_items.push(trait_item); } else if associated_type_overridden { invalidated_items.push(trait_item.name()); } @@ -1114,16 +1114,20 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } if !missing_items.is_empty() { - struct_span_err!(tcx.sess, impl_span, E0046, + let mut err = struct_span_err!(tcx.sess, impl_span, E0046, "not all trait items implemented, missing: `{}`", missing_items.iter() - .map(|name| name.to_string()) - .collect::>().join("`, `")) - .span_label(impl_span, &format!("missing `{}` in implementation", + .map(|trait_item| trait_item.name().to_string()) + .collect::>().join("`, `")); + err.span_label(impl_span, &format!("missing `{}` in implementation", missing_items.iter() - .map(|name| name.to_string()) + .map(|name| name.name().to_string()) .collect::>().join("`, `")) - ).emit(); + ); + for trait_item in missing_items { + err.note(&(trait_item.signature(tcx))); + } + err.emit(); } if !invalidated_items.is_empty() { diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index b176b8fefc612..44ed647c01509 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -646,6 +646,63 @@ impl CodeMap { } } + /// Reformat `sp`'s snippet to oneline if it is available + /// + /// Given a snippet like: + /// + /// ```text + /// fn foo< 'lifetime, T >( + /// &self, + /// bar : &Type< 'lifetime, T>) + /// -> std::result::Result<(), + /// Error>; + /// ``` + /// + /// it'll return: + /// + /// ```text + /// fn foo<'lifetime, T>(&self, bar: &Type<'lifetime, T>) -> std::result::Result<(), Error>; + /// ``` + pub fn span_to_oneline_snippet(&self, sp: Span) -> Result { + let no_space_after = ["<", "("]; + let no_space_before = [">", ")", ",", ":", ";"]; + + let snippet = self.span_to_snippet(sp); + match snippet { + Ok(snippet) => { + let mut it = snippet.split_whitespace(); + let mut next = it.next(); + let mut result = String::new(); + + loop { // Remove spaces after `<` and `(` and before `>`, `)` `:` and `,` + match next { + Some(c) => { + let peek = it.next(); + match peek { + Some(n) => { + result.push_str(c); + + if !(no_space_after.into_iter().any(|x| c.ends_with(x)) || + no_space_before.into_iter().any(|x| n.starts_with(x))) { + result.push_str(" "); + } + next = peek; + } + None => { // last item, don't skip + result.push_str(c); + next = peek; + } + } + } + None => break, // end of iter + } + } + Ok(result) + } + Err(e) => Err(e), + } + } + pub fn get_filemap(&self, filename: &str) -> Option> { for fm in self.files.borrow().iter() { if filename == fm.name { diff --git a/src/test/compile-fail/E0046.rs b/src/test/compile-fail/E0046.rs index a8b56b2b9ab37..7747318335e7c 100644 --- a/src/test/compile-fail/E0046.rs +++ b/src/test/compile-fail/E0046.rs @@ -17,6 +17,7 @@ struct Bar; impl Foo for Bar {} //~^ ERROR E0046 //~| NOTE missing `foo` in implementation +//~| NOTE fn foo(); fn main() { } diff --git a/src/test/compile-fail/impl-missing-items.rs b/src/test/compile-fail/impl-missing-items.rs new file mode 100644 index 0000000000000..7f6c6d7c9beb3 --- /dev/null +++ b/src/test/compile-fail/impl-missing-items.rs @@ -0,0 +1,48 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(associated_consts)] + +use std::str::FromStr; + +struct A {} + +trait X { + type Foo; + const BAR: u32 = 128; + + fn foo() -> T; + fn bar(); + fn bay< + 'lifetime, TypeParameterA + >( a : usize, + b: u8 ); +} + +impl std::fmt::Display for A { +//~^ ERROR not all trait items implemented, missing: `fmt` +//~| NOTE missing `fmt` in implementation +//~| NOTE fn fmt(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>; + +} +impl FromStr for A{} +//~^ ERROR not all trait items implemented, missing: `Err`, `from_str` +//~| NOTE missing `Err`, `from_str` in implementation +//~| NOTE type Err; +//~| NOTE fn from_str(&str) -> std::result::Result::Err>; + +impl X for A { +//~^ ERROR not all trait items implemented, missing: `Foo`, `foo`, `bar`, `bay` +//~| NOTE missing `Foo`, `foo`, `bar`, `bay` in implementation +//~| NOTE type Foo; +//~| NOTE fn foo() -> T; +//~| NOTE fn bar(); +//~| NOTE fn bay<'lifetime, TypeParameterA>(a: usize, b: u8); +} diff --git a/src/test/compile-fail/impl-wrong-item-for-trait.rs b/src/test/compile-fail/impl-wrong-item-for-trait.rs index 388c9a1729cca..ddf9c589f3b73 100644 --- a/src/test/compile-fail/impl-wrong-item-for-trait.rs +++ b/src/test/compile-fail/impl-wrong-item-for-trait.rs @@ -22,6 +22,7 @@ pub struct FooConstForMethod; impl Foo for FooConstForMethod { //~^ ERROR E0046 //~| NOTE missing `bar` in implementation + //~| NOTE fn bar(&self); const bar: u64 = 1; //~^ ERROR E0323 //~| NOTE does not match trait @@ -33,6 +34,7 @@ pub struct FooMethodForConst; impl Foo for FooMethodForConst { //~^ ERROR E0046 //~| NOTE missing `MY_CONST` in implementation + //~| NOTE const MY_CONST: u32; fn bar(&self) {} fn MY_CONST() {} //~^ ERROR E0324 @@ -44,6 +46,7 @@ pub struct FooTypeForMethod; impl Foo for FooTypeForMethod { //~^ ERROR E0046 //~| NOTE missing `bar` in implementation + //~| NOTE fn bar(&self); type bar = u64; //~^ ERROR E0325 //~| NOTE does not match trait diff --git a/src/test/compile-fail/issue-23729.rs b/src/test/compile-fail/issue-23729.rs index b1047ce18cccd..fd9c2e2fe78a4 100644 --- a/src/test/compile-fail/issue-23729.rs +++ b/src/test/compile-fail/issue-23729.rs @@ -20,6 +20,7 @@ fn main() { impl Iterator for Recurrence { //~^ ERROR E0046 //~| NOTE missing `Item` in implementation + //~| NOTE type Item; #[inline] fn next(&mut self) -> Option { if self.pos < 2 { diff --git a/src/test/compile-fail/issue-23827.rs b/src/test/compile-fail/issue-23827.rs index 2062e2373129b..fbb44181a4310 100644 --- a/src/test/compile-fail/issue-23827.rs +++ b/src/test/compile-fail/issue-23827.rs @@ -36,6 +36,7 @@ impl FnMut<(C,)> for Prototype { impl FnOnce<(C,)> for Prototype { //~^ ERROR E0046 //~| NOTE missing `Output` in implementation + //~| NOTE type Output; extern "rust-call" fn call_once(self, (comp,): (C,)) -> Prototype { Fn::call(&self, (comp,)) } diff --git a/src/test/compile-fail/issue-24356.rs b/src/test/compile-fail/issue-24356.rs index d39fd539dcebc..40c01491e7587 100644 --- a/src/test/compile-fail/issue-24356.rs +++ b/src/test/compile-fail/issue-24356.rs @@ -30,6 +30,7 @@ fn main() { impl Deref for Thing { //~^ ERROR E0046 //~| NOTE missing `Target` in implementation + //~| NOTE type Target; fn deref(&self) -> i8 { self.0 } }