Skip to content

Include type of missing trait methods in error #36371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Show type of missing/incorrect trait methods
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`
  --> foo.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`
  --> foo.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`
  --> foo.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

For a given file `foo.rs`:

```rust
struct A {}

impl std::fmt::Display for A {
    fn fmt() -> () {}
}
```

provide the expected method signature:

```bash
error: main function not found

error[E0186]: method `fmt` has a `&self` declaration in the trait, but not in the impl
 --> foo.rs:4:5
  |
4 |     fn fmt() -> () {}
  |     ^^^^^^^^^^^^^^^^^ expected `&self` in impl
  |
  = note: Expected signature: fn fmt(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>;
  = note:    Found signature: fn fmt();

error: aborting due to previous error
```

Fixes #28011
  • Loading branch information
estebank committed Oct 1, 2016
commit f9c0319569fab065e0bf070524138e205716ff54
21 changes: 21 additions & 0 deletions src/librustc/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,27 @@ pub enum Node<'ast> {
NodeTyParam(&'ast TyParam)
}

impl<'ast> Node<'ast> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add spans to everything?

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 @@ -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(),
},
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, Debug, PartialEq, Eq, Copy, RustcEncodable, RustcDecodable)]
Expand Down Expand Up @@ -327,6 +361,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 @@ -354,6 +416,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 @@ -364,6 +438,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
2 changes: 2 additions & 0 deletions src/librustc_typeck/check/compare_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
err.span_label(span, & format!("`{}` used in trait",
trait_m.explicit_self));
}
err.note(&format!("Expected signature: {}", trait_m.signature()));
err.note(&format!(" Found signature: {}", impl_m.signature()));
err.emit();
return;
}
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 @@ -1105,24 +1105,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 @@ -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<String, SpanSnippetError> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nikomatsakis could there be a method already in the codebase that does this? If there isn't, does this seem reasonable to be added for these use cases?

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