Skip to content

Commit

Permalink
Include type of missing trait methods in error
Browse files Browse the repository at this point in the history
For a given file `foo.rs`:

```rust
use std::str::FromStr;

struct A {}

trait X<T> {
    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 {
}
impl FromStr for A{}

impl X<usize> for A {
}
```

Provide the following output:

```bash
error: main function not found

error[E0046]: not all trait items implemented, missing: `fmt`
  --> file2.rs:18:1
   |
18 | impl std::fmt::Display for A {
   | ^ missing `fmt` in implementation
   |
   = note: fn fmt(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>;

error[E0046]: not all trait items implemented, missing: `Err`, `from_str`
  --> file2.rs:20:1
   |
20 | impl FromStr for A{}
   | ^^^^^^^^^^^^^^^^^^^^ missing `Err`, `from_str` in implementation
   |
   = note: type Err;
   = note: fn from_str(&str) -> std::result::Result<Self, <Self as std::str::FromStr>::Err>;

error[E0046]: not all trait items implemented, missing: `Foo`, `foo`, `bar`, `bay`
  --> file2.rs:22:1
   |
22 | impl X<usize> for A {
   | ^ 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);

error: aborting due to 3 previous errors
```

Fixes #24626
  • Loading branch information
estebank committed Sep 10, 2016
1 parent d748fa6 commit cc0c258
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 7 deletions.
21 changes: 21 additions & 0 deletions src/librustc/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,27 @@ pub enum Node<'ast> {
NodeTyParam(&'ast TyParam)
}

impl<'ast> Node<'ast> {
pub fn span(&self) -> Option<Span> {
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)]
Expand Down
81 changes: 81 additions & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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::<Vec<String>>()
.join(", "))
} else {
String::new()
};
let args = self.fty.sig.inputs().0.iter()
.map(|t| format!("{:?}", t)).collect::<Vec<_>>().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> {
Expand Down Expand Up @@ -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 {
" = <DEFAULT>"
} else {
""
};
format!("const {}: {:?}{};", self.name.to_string(), self.ty, value)
}
}

#[derive(Clone, Copy, Debug)]
pub struct AssociatedType<'tcx> {
pub name: Name,
Expand All @@ -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<A> <: T<B> iff A <: B -- e.g., function return type
Expand Down
18 changes: 11 additions & 7 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,24 +1106,28 @@ 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());
}
}
}

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::<Vec<_>>().join("`, `"))
.span_label(impl_span, &format!("missing `{}` in implementation",
.map(|trait_item| trait_item.name().to_string())
.collect::<Vec<_>>().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::<Vec<_>>().join("`, `"))
).emit();
);
for trait_item in missing_items {
err.note(&(trait_item.signature(tcx)));
}
err.emit();
}

if !invalidated_items.is_empty() {
Expand Down
57 changes: 57 additions & 0 deletions src/libsyntax/codemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, SpanSnippetError> {
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<Rc<FileMap>> {
for fm in self.files.borrow().iter() {
if filename == fm.name {
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/E0046.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct Bar;
impl Foo for Bar {}
//~^ ERROR E0046
//~| NOTE missing `foo` in implementation
//~| NOTE fn foo();

fn main() {
}
48 changes: 48 additions & 0 deletions src/test/compile-fail/impl-missing-items.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> {
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<Self, <Self as std::str::FromStr>::Err>;

impl X<usize> 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);
}
3 changes: 3 additions & 0 deletions src/test/compile-fail/impl-wrong-item-for-trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/issue-23729.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64> {
if self.pos < 2 {
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/issue-23827.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl<C: Component> FnMut<(C,)> for Prototype {
impl<C: Component> 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,))
}
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/issue-24356.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}

Expand Down

0 comments on commit cc0c258

Please sign in to comment.