diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 39114ec4238e7..d05b3de963439 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -60,6 +60,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 717b8923a1635..7d2972a9ed705 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -235,6 +235,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(tcx), + }, + None => item.signature(tcx), + } + } + None => item.signature(tcx), + } + } + TypeTraitItem(ref item) => item.signature(tcx), + 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(tcx), + }, + None => item.signature(tcx), + } + } + None => item.signature(tcx), + } + } + } + } } #[derive(Clone, Debug, PartialEq, Eq, Copy, RustcEncodable, RustcDecodable)] @@ -327,6 +361,36 @@ impl<'tcx> Method<'tcx> { ImplContainer(id) => id, } } + + pub fn signature<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> 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!("{}", tcx.item_name(self.fty.sig.output().0.def_id).as_str()); + 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) + //format!("{}fn {}", unsafety, self.fty.sig.0)//name, type_args, args, return_signature) + } } impl<'tcx> PartialEq for Method<'tcx> { @@ -354,6 +418,19 @@ pub struct AssociatedConst<'tcx> { pub has_value: bool } +impl<'tcx> AssociatedConst<'tcx> { + pub fn signature<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> String { + // const FOO: Type = DEFAULT; + let value = if self.has_value { + " = " + } else { + "" + }; + //format!("const {}: {}{};", self.name.to_string(), tcx.item_name(self.ty.def_id).as_str(), value) + format!("const {}: {:?}{};", self.name.to_string(), self.ty, value) + } +} + #[derive(Clone, Copy, Debug)] pub struct AssociatedType<'tcx> { pub name: Name, @@ -364,6 +441,13 @@ pub struct AssociatedType<'tcx> { pub container: ImplOrTraitItemContainer, } +impl<'tcx> AssociatedType<'tcx> { + pub fn signature<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> 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_errors/emitter.rs b/src/librustc_errors/emitter.rs index 1d7ff45b3b8a0..553a3dce0e41b 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -109,22 +109,26 @@ impl EmitterWriter { fn add_annotation_to_file(file_vec: &mut Vec, file: Rc, line_index: usize, - ann: Annotation) { + annotation: Option) { + let ann = match annotation { + Some(ref ann) => vec![ann.clone()], + None => vec![] + }; for slot in file_vec.iter_mut() { // Look through each of our files for the one we're adding to if slot.file.name == file.name { // See if we already have a line for it for line_slot in &mut slot.lines { - if line_slot.line_index == line_index { - line_slot.annotations.push(ann); + if line_slot.line_index == line_index && annotation.is_some() { + line_slot.annotations.push(annotation.unwrap()); return; } } // We don't have a line yet, create one slot.lines.push(Line { line_index: line_index, - annotations: vec![ann], + annotations: ann, }); slot.lines.sort(); return; @@ -135,7 +139,7 @@ impl EmitterWriter { file: file, lines: vec![Line { line_index: line_index, - annotations: vec![ann], + annotations: ann, }], }); } @@ -151,6 +155,8 @@ impl EmitterWriter { let mut hi = cm.lookup_char_pos(span_label.span.hi); let mut is_minimized = false; + let start = lo.line; + let end = hi.line + 1; // If the span is multi-line, simplify down to the span of one character if lo.line != hi.line { hi.line = lo.line; @@ -168,15 +174,24 @@ impl EmitterWriter { } add_annotation_to_file(&mut output, - lo.file, + lo.file.clone(), lo.line, - Annotation { + Some(Annotation { start_col: lo.col.0, end_col: hi.col.0, is_primary: span_label.is_primary, is_minimized: is_minimized, label: span_label.label.clone(), - }); + })); + if start != end { + // Add the rest of the lines, without any annotation + for line in start+1..end { + add_annotation_to_file(&mut output, + lo.file.clone(), + line, + None); + } + } } } output diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index bc599a8207656..be16bc37f4ac6 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -52,7 +52,7 @@ pub mod registry; pub mod styled_buffer; mod lock; -use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION }; +use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION}; use syntax_pos::{MacroBacktrace}; #[derive(Clone)] diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 826a88127d84e..14abd6f0e211f 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -87,6 +87,7 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, err.span_label(span, & format!("`{}` used in trait", trait_m.explicit_self)); } + err.emit(); return; } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ee00cb2f5a3e4..31b639872e2b1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1105,7 +1105,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()); } @@ -1113,16 +1113,36 @@ 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 codemap = tcx.sess.codemap(); + let start = codemap.lookup_line(impl_span.lo); + let end = codemap.lookup_line(impl_span.hi); + let sp = if let (Ok(start), Ok(end)) = (start, end) { + if start.line != end.line { + impl_span.start_point() + } else { + impl_span + } + } else { + impl_span + }; + let mut err = struct_span_err!(tcx.sess, sp, 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(sp, &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 { + if let Some(span) = tcx.map.span_if_local(trait_item.def_id()) { + err.span_label(span, &"missing definition in implementation"); + } else { + err.note(&format!("infered definition: `{}`", trait_item.signature(tcx))); + } + } + err.emit(); } if !invalidated_items.is_empty() { diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 6d68ce3646d53..863af152afce1 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -342,7 +342,7 @@ impl CodeMap { } // If the relevant filemap is empty, we don't return a line number. - fn lookup_line(&self, pos: BytePos) -> Result> { + pub fn lookup_line(&self, pos: BytePos) -> Result> { let idx = self.lookup_filemap_idx(pos); let files = self.files.borrow(); @@ -670,6 +670,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/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 13dcf7b188b70..b840d98de53d6 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -80,6 +80,12 @@ impl Span { Span { lo: BytePos(lo), hi: self.hi, expn_id: self.expn_id} } + /// Returns a new span representing just the start-point of this span + pub fn start_point(self) -> Span { + let lo = cmp::min(self.lo.0, self.hi.0 - 1); + Span { lo: BytePos(lo), hi: BytePos(lo), expn_id: self.expn_id} + } + /// Returns `self` if `self` is not the dummy span, and `other` otherwise. pub fn substitute_dummy(self, other: Span) -> Span { if self.source_equal(&DUMMY_SP) { other } else { self } 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 } }