Skip to content

Commit f9c0319

Browse files
committed
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
1 parent 289f3a4 commit f9c0319

File tree

11 files changed

+227
-7
lines changed

11 files changed

+227
-7
lines changed

src/librustc/hir/map/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ pub enum Node<'ast> {
6060
NodeTyParam(&'ast TyParam)
6161
}
6262

63+
impl<'ast> Node<'ast> {
64+
pub fn span(&self) -> Option<Span> {
65+
match *self {
66+
NodeItem(item) => Some(item.span),
67+
NodeForeignItem(item) => Some(item.span),
68+
NodeTraitItem(item) => Some(item.span),
69+
NodeImplItem(item) => Some(item.span),
70+
NodeVariant(_) => None,
71+
NodeExpr(item) => Some(item.span),
72+
NodeStmt(_) => None,
73+
NodeTy(ty) => Some(ty.span),
74+
NodeLocal(pat) => Some(pat.span),
75+
NodePat(pat) => Some(pat.span),
76+
NodeBlock(block) => Some(block.span),
77+
NodeStructCtor(_) => None,
78+
NodeLifetime(lifetime) => Some(lifetime.span),
79+
NodeTyParam(ty) => Some(ty.span),
80+
}
81+
}
82+
}
83+
6384
/// Represents an entry and its parent NodeID.
6485
/// The odd layout is to bring down the total size.
6586
#[derive(Copy, Debug)]

src/librustc/ty/mod.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,40 @@ impl<'tcx> ImplOrTraitItem<'tcx> {
235235
_ => None,
236236
}
237237
}
238+
239+
pub fn signature<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> String {
240+
match *self {
241+
MethodTraitItem(ref item) => {
242+
match tcx.map.get_if_local(item.def_id) {
243+
Some(node) => {
244+
match node.span() {
245+
Some(span) => match tcx.sess.codemap().span_to_oneline_snippet(span) {
246+
Ok(snippet) => snippet,
247+
Err(_) => item.signature(),
248+
},
249+
None => item.signature(),
250+
}
251+
}
252+
None => item.signature(),
253+
}
254+
}
255+
TypeTraitItem(ref item) => item.signature(),
256+
ConstTraitItem(ref item) => {
257+
match tcx.map.get_if_local(item.def_id) {
258+
Some(node) => {
259+
match node.span() {
260+
Some(span) => match tcx.sess.codemap().span_to_oneline_snippet(span) {
261+
Ok(snippet) => snippet,
262+
Err(_) => item.signature(),
263+
},
264+
None => item.signature(),
265+
}
266+
}
267+
None => item.signature(),
268+
}
269+
}
270+
}
271+
}
238272
}
239273

240274
#[derive(Clone, Debug, PartialEq, Eq, Copy, RustcEncodable, RustcDecodable)]
@@ -327,6 +361,34 @@ impl<'tcx> Method<'tcx> {
327361
ImplContainer(id) => id,
328362
}
329363
}
364+
365+
pub fn signature(&self) -> String {
366+
let name = self.name.to_string();
367+
let unsafety = match self.fty.unsafety {
368+
hir::Unsafety::Unsafe => "unsafe ",
369+
hir::Unsafety::Normal => "",
370+
};
371+
let has_gen_types = !self.generics.types.is_empty();
372+
let type_args = if has_gen_types {
373+
format!("<{}>", self.generics.types.clone().into_iter()
374+
.map(|t| t.name.as_str().to_string())
375+
.collect::<Vec<String>>()
376+
.join(", "))
377+
} else {
378+
String::new()
379+
};
380+
let args = self.fty.sig.inputs().0.iter()
381+
.map(|t| format!("{:?}", t)).collect::<Vec<_>>().join(", ");
382+
let return_type = format!("{:?}", self.fty.sig.output().0);
383+
let return_signature = if &return_type == "()" {
384+
"".to_string()
385+
} else {
386+
format!(" -> {}", return_type)
387+
};
388+
389+
// unsafe fn name<'a, T>(args) -> ReturnType
390+
format!("{}fn {}{}({}){};", unsafety, name, type_args, args, return_signature)
391+
}
330392
}
331393

332394
impl<'tcx> PartialEq for Method<'tcx> {
@@ -354,6 +416,18 @@ pub struct AssociatedConst<'tcx> {
354416
pub has_value: bool
355417
}
356418

419+
impl<'tcx> AssociatedConst<'tcx> {
420+
pub fn signature(&self) -> String {
421+
// const FOO: Type = DEFAULT;
422+
let value = if self.has_value {
423+
" = <DEFAULT>"
424+
} else {
425+
""
426+
};
427+
format!("const {}: {:?}{};", self.name.to_string(), self.ty, value)
428+
}
429+
}
430+
357431
#[derive(Clone, Copy, Debug)]
358432
pub struct AssociatedType<'tcx> {
359433
pub name: Name,
@@ -364,6 +438,13 @@ pub struct AssociatedType<'tcx> {
364438
pub container: ImplOrTraitItemContainer,
365439
}
366440

441+
impl<'tcx> AssociatedType<'tcx> {
442+
pub fn signature(&self) -> String {
443+
//// type Type;
444+
format!("type {};", self.name.to_string())
445+
}
446+
}
447+
367448
#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable, Copy)]
368449
pub enum Variance {
369450
Covariant, // T<A> <: T<B> iff A <: B -- e.g., function return type

src/librustc_typeck/check/compare_method.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
8787
err.span_label(span, & format!("`{}` used in trait",
8888
trait_m.explicit_self));
8989
}
90+
err.note(&format!("Expected signature: {}", trait_m.signature()));
91+
err.note(&format!(" Found signature: {}", impl_m.signature()));
9092
err.emit();
9193
return;
9294
}

src/librustc_typeck/check/mod.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,24 +1105,28 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
11051105

11061106
if !is_implemented {
11071107
if !is_provided {
1108-
missing_items.push(trait_item.name());
1108+
missing_items.push(trait_item);
11091109
} else if associated_type_overridden {
11101110
invalidated_items.push(trait_item.name());
11111111
}
11121112
}
11131113
}
11141114

11151115
if !missing_items.is_empty() {
1116-
struct_span_err!(tcx.sess, impl_span, E0046,
1116+
let mut err = struct_span_err!(tcx.sess, impl_span, E0046,
11171117
"not all trait items implemented, missing: `{}`",
11181118
missing_items.iter()
1119-
.map(|name| name.to_string())
1120-
.collect::<Vec<_>>().join("`, `"))
1121-
.span_label(impl_span, &format!("missing `{}` in implementation",
1119+
.map(|trait_item| trait_item.name().to_string())
1120+
.collect::<Vec<_>>().join("`, `"));
1121+
err.span_label(impl_span, &format!("missing `{}` in implementation",
11221122
missing_items.iter()
1123-
.map(|name| name.to_string())
1123+
.map(|name| name.name().to_string())
11241124
.collect::<Vec<_>>().join("`, `"))
1125-
).emit();
1125+
);
1126+
for trait_item in missing_items {
1127+
err.note(&(trait_item.signature(tcx)));
1128+
}
1129+
err.emit();
11261130
}
11271131

11281132
if !invalidated_items.is_empty() {

src/libsyntax/codemap.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,63 @@ impl CodeMap {
670670
}
671671
}
672672

673+
/// Reformat `sp`'s snippet to oneline if it is available
674+
///
675+
/// Given a snippet like:
676+
///
677+
/// ```text
678+
/// fn foo< 'lifetime, T >(
679+
/// &self,
680+
/// bar : &Type< 'lifetime, T>)
681+
/// -> std::result::Result<(),
682+
/// Error>;
683+
/// ```
684+
///
685+
/// it'll return:
686+
///
687+
/// ```text
688+
/// fn foo<'lifetime, T>(&self, bar: &Type<'lifetime, T>) -> std::result::Result<(), Error>;
689+
/// ```
690+
pub fn span_to_oneline_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
691+
let no_space_after = ["<", "("];
692+
let no_space_before = [">", ")", ",", ":", ";"];
693+
694+
let snippet = self.span_to_snippet(sp);
695+
match snippet {
696+
Ok(snippet) => {
697+
let mut it = snippet.split_whitespace();
698+
let mut next = it.next();
699+
let mut result = String::new();
700+
701+
loop { // Remove spaces after `<` and `(` and before `>`, `)` `:` and `,`
702+
match next {
703+
Some(c) => {
704+
let peek = it.next();
705+
match peek {
706+
Some(n) => {
707+
result.push_str(c);
708+
709+
if !(no_space_after.into_iter().any(|x| c.ends_with(x)) ||
710+
no_space_before.into_iter().any(|x| n.starts_with(x))) {
711+
result.push_str(" ");
712+
}
713+
next = peek;
714+
}
715+
None => { // last item, don't skip
716+
result.push_str(c);
717+
next = peek;
718+
}
719+
}
720+
}
721+
None => break, // end of iter
722+
}
723+
}
724+
Ok(result)
725+
}
726+
Err(e) => Err(e),
727+
}
728+
}
729+
673730
pub fn get_filemap(&self, filename: &str) -> Option<Rc<FileMap>> {
674731
for fm in self.files.borrow().iter() {
675732
if filename == fm.name {

src/test/compile-fail/E0046.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ struct Bar;
1717
impl Foo for Bar {}
1818
//~^ ERROR E0046
1919
//~| NOTE missing `foo` in implementation
20+
//~| NOTE fn foo();
2021

2122
fn main() {
2223
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(associated_consts)]
12+
13+
use std::str::FromStr;
14+
15+
struct A {}
16+
17+
trait X<T> {
18+
type Foo;
19+
const BAR: u32 = 128;
20+
21+
fn foo() -> T;
22+
fn bar();
23+
fn bay<
24+
'lifetime, TypeParameterA
25+
>( a : usize,
26+
b: u8 );
27+
}
28+
29+
impl std::fmt::Display for A {
30+
//~^ ERROR not all trait items implemented, missing: `fmt`
31+
//~| NOTE missing `fmt` in implementation
32+
//~| NOTE fn fmt(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>;
33+
34+
}
35+
impl FromStr for A{}
36+
//~^ ERROR not all trait items implemented, missing: `Err`, `from_str`
37+
//~| NOTE missing `Err`, `from_str` in implementation
38+
//~| NOTE type Err;
39+
//~| NOTE fn from_str(&str) -> std::result::Result<Self, <Self as std::str::FromStr>::Err>;
40+
41+
impl X<usize> for A {
42+
//~^ ERROR not all trait items implemented, missing: `Foo`, `foo`, `bar`, `bay`
43+
//~| NOTE missing `Foo`, `foo`, `bar`, `bay` in implementation
44+
//~| NOTE type Foo;
45+
//~| NOTE fn foo() -> T;
46+
//~| NOTE fn bar();
47+
//~| NOTE fn bay<'lifetime, TypeParameterA>(a: usize, b: u8);
48+
}

src/test/compile-fail/impl-wrong-item-for-trait.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub struct FooConstForMethod;
2222
impl Foo for FooConstForMethod {
2323
//~^ ERROR E0046
2424
//~| NOTE missing `bar` in implementation
25+
//~| NOTE fn bar(&self);
2526
const bar: u64 = 1;
2627
//~^ ERROR E0323
2728
//~| NOTE does not match trait
@@ -33,6 +34,7 @@ pub struct FooMethodForConst;
3334
impl Foo for FooMethodForConst {
3435
//~^ ERROR E0046
3536
//~| NOTE missing `MY_CONST` in implementation
37+
//~| NOTE const MY_CONST: u32;
3638
fn bar(&self) {}
3739
fn MY_CONST() {}
3840
//~^ ERROR E0324
@@ -44,6 +46,7 @@ pub struct FooTypeForMethod;
4446
impl Foo for FooTypeForMethod {
4547
//~^ ERROR E0046
4648
//~| NOTE missing `bar` in implementation
49+
//~| NOTE fn bar(&self);
4750
type bar = u64;
4851
//~^ ERROR E0325
4952
//~| NOTE does not match trait

src/test/compile-fail/issue-23729.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ fn main() {
2020
impl Iterator for Recurrence {
2121
//~^ ERROR E0046
2222
//~| NOTE missing `Item` in implementation
23+
//~| NOTE type Item;
2324
#[inline]
2425
fn next(&mut self) -> Option<u64> {
2526
if self.pos < 2 {

src/test/compile-fail/issue-23827.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ impl<C: Component> FnMut<(C,)> for Prototype {
3636
impl<C: Component> FnOnce<(C,)> for Prototype {
3737
//~^ ERROR E0046
3838
//~| NOTE missing `Output` in implementation
39+
//~| NOTE type Output;
3940
extern "rust-call" fn call_once(self, (comp,): (C,)) -> Prototype {
4041
Fn::call(&self, (comp,))
4142
}

src/test/compile-fail/issue-24356.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ fn main() {
3030
impl Deref for Thing {
3131
//~^ ERROR E0046
3232
//~| NOTE missing `Target` in implementation
33+
//~| NOTE type Target;
3334
fn deref(&self) -> i8 { self.0 }
3435
}
3536

0 commit comments

Comments
 (0)