Skip to content

Commit cc0c258

Browse files
committed
Include type of missing trait methods in error
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
1 parent d748fa6 commit cc0c258

File tree

10 files changed

+225
-7
lines changed

10 files changed

+225
-7
lines changed

src/librustc/hir/map/mod.rs

+21
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,27 @@ pub enum Node<'ast> {
6262
NodeTyParam(&'ast TyParam)
6363
}
6464

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

src/librustc/ty/mod.rs

+81
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,40 @@ impl<'tcx> ImplOrTraitItem<'tcx> {
249249
_ => None,
250250
}
251251
}
252+
253+
pub fn signature<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> String {
254+
match *self {
255+
MethodTraitItem(ref item) => {
256+
match tcx.map.get_if_local(item.def_id) {
257+
Some(node) => {
258+
match node.span() {
259+
Some(span) => match tcx.sess.codemap().span_to_oneline_snippet(span) {
260+
Ok(snippet) => snippet,
261+
Err(_) => item.signature(),
262+
},
263+
None => item.signature(),
264+
}
265+
}
266+
None => item.signature(),
267+
}
268+
}
269+
TypeTraitItem(ref item) => item.signature(),
270+
ConstTraitItem(ref item) => {
271+
match tcx.map.get_if_local(item.def_id) {
272+
Some(node) => {
273+
match node.span() {
274+
Some(span) => match tcx.sess.codemap().span_to_oneline_snippet(span) {
275+
Ok(snippet) => snippet,
276+
Err(_) => item.signature(),
277+
},
278+
None => item.signature(),
279+
}
280+
}
281+
None => item.signature(),
282+
}
283+
}
284+
}
285+
}
252286
}
253287

254288
#[derive(Clone, Copy, Debug)]
@@ -380,6 +414,34 @@ impl<'tcx> Method<'tcx> {
380414
ImplContainer(id) => id,
381415
}
382416
}
417+
418+
pub fn signature(&self) -> String {
419+
let name = self.name.to_string();
420+
let unsafety = match self.fty.unsafety {
421+
hir::Unsafety::Unsafe => "unsafe ",
422+
hir::Unsafety::Normal => "",
423+
};
424+
let has_gen_types = !self.generics.types.is_empty();
425+
let type_args = if has_gen_types {
426+
format!("<{}>", self.generics.types.clone().into_iter()
427+
.map(|t| t.name.as_str().to_string())
428+
.collect::<Vec<String>>()
429+
.join(", "))
430+
} else {
431+
String::new()
432+
};
433+
let args = self.fty.sig.inputs().0.iter()
434+
.map(|t| format!("{:?}", t)).collect::<Vec<_>>().join(", ");
435+
let return_type = format!("{:?}", self.fty.sig.output().0);
436+
let return_signature = if &return_type == "()" {
437+
"".to_string()
438+
} else {
439+
format!(" -> {}", return_type)
440+
};
441+
442+
// unsafe fn name<'a, T>(args) -> ReturnType
443+
format!("{}fn {}{}({}){};", unsafety, name, type_args, args, return_signature)
444+
}
383445
}
384446

385447
impl<'tcx> PartialEq for Method<'tcx> {
@@ -407,6 +469,18 @@ pub struct AssociatedConst<'tcx> {
407469
pub has_value: bool
408470
}
409471

472+
impl<'tcx> AssociatedConst<'tcx> {
473+
pub fn signature(&self) -> String {
474+
// const FOO: Type = DEFAULT;
475+
let value = if self.has_value {
476+
" = <DEFAULT>"
477+
} else {
478+
""
479+
};
480+
format!("const {}: {:?}{};", self.name.to_string(), self.ty, value)
481+
}
482+
}
483+
410484
#[derive(Clone, Copy, Debug)]
411485
pub struct AssociatedType<'tcx> {
412486
pub name: Name,
@@ -417,6 +491,13 @@ pub struct AssociatedType<'tcx> {
417491
pub container: ImplOrTraitItemContainer,
418492
}
419493

494+
impl<'tcx> AssociatedType<'tcx> {
495+
pub fn signature(&self) -> String {
496+
//// type Type;
497+
format!("type {};", self.name.to_string())
498+
}
499+
}
500+
420501
#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable, Copy)]
421502
pub enum Variance {
422503
Covariant, // T<A> <: T<B> iff A <: B -- e.g., function return type

src/librustc_typeck/check/mod.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -1106,24 +1106,28 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
11061106

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

11161116
if !missing_items.is_empty() {
1117-
struct_span_err!(tcx.sess, impl_span, E0046,
1117+
let mut err = struct_span_err!(tcx.sess, impl_span, E0046,
11181118
"not all trait items implemented, missing: `{}`",
11191119
missing_items.iter()
1120-
.map(|name| name.to_string())
1121-
.collect::<Vec<_>>().join("`, `"))
1122-
.span_label(impl_span, &format!("missing `{}` in implementation",
1120+
.map(|trait_item| trait_item.name().to_string())
1121+
.collect::<Vec<_>>().join("`, `"));
1122+
err.span_label(impl_span, &format!("missing `{}` in implementation",
11231123
missing_items.iter()
1124-
.map(|name| name.to_string())
1124+
.map(|name| name.name().to_string())
11251125
.collect::<Vec<_>>().join("`, `"))
1126-
).emit();
1126+
);
1127+
for trait_item in missing_items {
1128+
err.note(&(trait_item.signature(tcx)));
1129+
}
1130+
err.emit();
11271131
}
11281132

11291133
if !invalidated_items.is_empty() {

src/libsyntax/codemap.rs

+57
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,63 @@ impl CodeMap {
646646
}
647647
}
648648

649+
/// Reformat `sp`'s snippet to oneline if it is available
650+
///
651+
/// Given a snippet like:
652+
///
653+
/// ```text
654+
/// fn foo< 'lifetime, T >(
655+
/// &self,
656+
/// bar : &Type< 'lifetime, T>)
657+
/// -> std::result::Result<(),
658+
/// Error>;
659+
/// ```
660+
///
661+
/// it'll return:
662+
///
663+
/// ```text
664+
/// fn foo<'lifetime, T>(&self, bar: &Type<'lifetime, T>) -> std::result::Result<(), Error>;
665+
/// ```
666+
pub fn span_to_oneline_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
667+
let no_space_after = ["<", "("];
668+
let no_space_before = [">", ")", ",", ":", ";"];
669+
670+
let snippet = self.span_to_snippet(sp);
671+
match snippet {
672+
Ok(snippet) => {
673+
let mut it = snippet.split_whitespace();
674+
let mut next = it.next();
675+
let mut result = String::new();
676+
677+
loop { // Remove spaces after `<` and `(` and before `>`, `)` `:` and `,`
678+
match next {
679+
Some(c) => {
680+
let peek = it.next();
681+
match peek {
682+
Some(n) => {
683+
result.push_str(c);
684+
685+
if !(no_space_after.into_iter().any(|x| c.ends_with(x)) ||
686+
no_space_before.into_iter().any(|x| n.starts_with(x))) {
687+
result.push_str(" ");
688+
}
689+
next = peek;
690+
}
691+
None => { // last item, don't skip
692+
result.push_str(c);
693+
next = peek;
694+
}
695+
}
696+
}
697+
None => break, // end of iter
698+
}
699+
}
700+
Ok(result)
701+
}
702+
Err(e) => Err(e),
703+
}
704+
}
705+
649706
pub fn get_filemap(&self, filename: &str) -> Option<Rc<FileMap>> {
650707
for fm in self.files.borrow().iter() {
651708
if filename == fm.name {

src/test/compile-fail/E0046.rs

+1
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
}
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

+3
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

+1
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

+1
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

+1
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)