From 335298e7b606c80cd7218de64856c10acf3855d5 Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Fri, 20 Nov 2015 11:11:08 +0100 Subject: [PATCH 1/6] Show similar trait implementations if no matching impl is found closes #21659 --- src/librustc/middle/traits/error_reporting.rs | 20 ++++++++++ .../issue-21659-show-relevant-trait-impls.rs | 39 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/test/compile-fail/issue-21659-show-relevant-trait-impls.rs diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index 9193b1a09f9fa..94e017a6855aa 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -225,6 +225,26 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, "the trait `{}` is not implemented for the type `{}`", trait_ref, trait_ref.self_ty()); + let mut counter = 1; + infcx.tcx.sess.fileline_help( + obligation.cause.span, + "the following implementations were found:"); + infcx.tcx.lookup_trait_def(trait_ref.def_id()).for_each_relevant_impl( + infcx.tcx, + trait_ref.self_ty(), + |impl_def_id| { + match infcx.tcx.impl_trait_ref(impl_def_id) { + Some(ref imp) => { + infcx.tcx.sess.fileline_help( + obligation.cause.span, + &format!("implementation {}: `{}`", counter, imp)); + counter += 1; + }, + None => (), + } + } + ); + // Check if it has a custom "#[rustc_on_unimplemented]" // error message, report with that message if it does let custom_note = report_on_unimplemented(infcx, &trait_ref.0, diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls.rs new file mode 100644 index 0000000000000..416eef4ad258f --- /dev/null +++ b/src/test/compile-fail/issue-21659-show-relevant-trait-impls.rs @@ -0,0 +1,39 @@ +// Copyright 2015 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. + +trait Foo { + fn foo(&self, a: A) -> A { + a + } +} + +trait NotRelevant { + fn nr(&self, a: A) -> A { + a + } +} + +struct Bar; + +impl Foo for Bar {} + +impl Foo for Bar {} + +impl NotRelevant for Bar {} + +fn main() { + let f1 = Bar; + + f1.foo(1usize); + //~^ error: the trait `Foo` is not implemented for the type `Bar` + // | help: the following implementations were found: + // | help: implementation 1: `Foo` + // | help: implementation 2: `Foo` +} From 2c52cb424a3fdad4888dd7b73eb2c19dbd75fc2d Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Sat, 28 Nov 2015 11:41:52 +0100 Subject: [PATCH 2/6] Limit displaying relevant trait impls to 4 --- src/librustc/middle/traits/error_reporting.rs | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index 94e017a6855aa..ef8982219563b 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -30,6 +30,7 @@ use middle::ty::{self, ToPredicate, HasTypeFlags, ToPolyTraitRef, TraitRef, Ty}; use middle::ty::fold::TypeFoldable; use util::nodemap::{FnvHashMap, FnvHashSet}; +use std::cmp; use std::fmt; use syntax::attr::{AttributeMethods, AttrMetaMethods}; use syntax::codemap::Span; @@ -225,38 +226,49 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, "the trait `{}` is not implemented for the type `{}`", trait_ref, trait_ref.self_ty()); - let mut counter = 1; - infcx.tcx.sess.fileline_help( - obligation.cause.span, - "the following implementations were found:"); - infcx.tcx.lookup_trait_def(trait_ref.def_id()).for_each_relevant_impl( - infcx.tcx, - trait_ref.self_ty(), - |impl_def_id| { - match infcx.tcx.impl_trait_ref(impl_def_id) { - Some(ref imp) => { - infcx.tcx.sess.fileline_help( - obligation.cause.span, - &format!("implementation {}: `{}`", counter, imp)); - counter += 1; - }, - None => (), - } - } - ); - // Check if it has a custom "#[rustc_on_unimplemented]" // error message, report with that message if it does let custom_note = report_on_unimplemented(infcx, &trait_ref.0, obligation.cause.span); if let Some(s) = custom_note { err.fileline_note(obligation.cause.span, &s); + } else { + let mut impl_candidates = Vec::new(); + infcx.tcx.lookup_trait_def(trait_ref.def_id()) + .for_each_relevant_impl( + infcx.tcx, + trait_ref.self_ty(), + |impl_def_id| { + match infcx.tcx.impl_trait_ref(impl_def_id) { + Some(ref imp) => { + impl_candidates.push(format!(" {}", imp)); + }, + None => (), + } + } + ); + + if impl_candidates.len() > 0 { + err.fileline_help( + obligation.cause.span, + &format!("the following implementations were found:")); + + let end = cmp::min(4, impl_candidates.len()); + for candidate in &impl_candidates[0..end] { + err.fileline_help(obligation.cause.span, + candidate); + } + if impl_candidates.len() > 4 { + err.fileline_help(obligation.cause.span, + &format!("and {} others", + impl_candidates.len()-4)); + } + } } note_obligation_cause(infcx, &mut err, obligation); err.emit(); } - } - + }, ty::Predicate::Equate(ref predicate) => { let predicate = infcx.resolve_type_vars_if_possible(predicate); let err = infcx.equality_predicate(obligation.cause.span, From 6093ea8039d4bf48ef2b34ca0ec1397016bb2b52 Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Sat, 28 Nov 2015 11:42:25 +0100 Subject: [PATCH 3/6] Add more tests --- ...ssue-21659-show-relevant-trait-impls-1.rs} | 0 ...issue-21659-show-relevant-trait-impls-2.rs | 46 +++++++++++++++++++ ...issue-21659-show-relevant-trait-impls-3.rs | 34 ++++++++++++++ 3 files changed, 80 insertions(+) rename src/test/compile-fail/{issue-21659-show-relevant-trait-impls.rs => issue-21659-show-relevant-trait-impls-1.rs} (100%) create mode 100644 src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs create mode 100644 src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs similarity index 100% rename from src/test/compile-fail/issue-21659-show-relevant-trait-impls.rs rename to src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs new file mode 100644 index 0000000000000..07a7c98dd7ff6 --- /dev/null +++ b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs @@ -0,0 +1,46 @@ +// Copyright 2015 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. + +trait Foo { + fn foo(&self, a: A) -> A { + a + } +} + +trait NotRelevant { + fn nr(&self, a: A) -> A { + a + } +} + +struct Bar; + +impl Foo for Bar {} +impl Foo for Bar {} +impl Foo for Bar {} + +impl Foo for Bar {} +impl Foo for Bar {} +impl Foo for Bar {} + +impl NotRelevant for Bar {} + +fn main() { + let f1 = Bar; + + f1.foo(1usize); + //~^ error: the trait `Foo` is not implemented for the type `Bar` + // | help: the following implementations were found: + // | help: Foo + // | help: Foo + // | help: Foo + // | help: Foo + // | help: and 2 others +} diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs new file mode 100644 index 0000000000000..0bb944edb9d84 --- /dev/null +++ b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs @@ -0,0 +1,34 @@ +// Copyright 2015 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. + +trait Foo { + fn foo(&self, a: A) -> A { + a + } +} + +trait NotRelevant { + fn nr(&self, a: A) -> A { + a + } +} + +struct Bar; + +impl NotRelevant for Bar {} + +fn main() { + let f1 = Bar; + + f1.foo(1usize); + //~^ error: method named `foo` found for type `Bar` in the current scope + //~| help: items from traits can only be used if the trait is implemented and in scope + //~| help: candidate #1: `Foo` +} From ba24fbd404d7489298af68b4384d1544cd54844d Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Thu, 17 Dec 2015 23:58:56 +0100 Subject: [PATCH 4/6] Manually check trait implementations --- src/librustc/middle/traits/error_reporting.rs | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index ef8982219563b..f4235f3df21c6 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -27,6 +27,7 @@ use fmt_macros::{Parser, Piece, Position}; use middle::def_id::DefId; use middle::infer::InferCtxt; use middle::ty::{self, ToPredicate, HasTypeFlags, ToPolyTraitRef, TraitRef, Ty}; +use middle::ty::fast_reject; use middle::ty::fold::TypeFoldable; use util::nodemap::{FnvHashMap, FnvHashSet}; @@ -233,20 +234,38 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, if let Some(s) = custom_note { err.fileline_note(obligation.cause.span, &s); } else { - let mut impl_candidates = Vec::new(); - infcx.tcx.lookup_trait_def(trait_ref.def_id()) - .for_each_relevant_impl( - infcx.tcx, - trait_ref.self_ty(), - |impl_def_id| { - match infcx.tcx.impl_trait_ref(impl_def_id) { - Some(ref imp) => { - impl_candidates.push(format!(" {}", imp)); - }, - None => (), + infcx.tcx.populate_implementations_for_trait_if_necessary( + trait_ref.def_id()); + + let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id()); + let blanket_impls = trait_def.blanket_impls.borrow(); + let impl_iter = blanket_impls.iter() + .filter_map(|&id| + infcx.tcx.impl_trait_ref(id)); + + let nonblanket = trait_def.nonblanket_impls.borrow(); + let nonblanket_iter = nonblanket.values() + .flat_map(|ids| + ids.iter().filter_map(|&id| + infcx.tcx.impl_trait_ref(id))); + + let simp = fast_reject::simplify_type(infcx.tcx, trait_ref.self_ty(), true); + let nonblanket_iter = nonblanket_iter.filter(|def| { + if let Some(simp) = simp { + let imp_simp = fast_reject::simplify_type(infcx.tcx, def.self_ty(), true); + if let Some(imp_simp) = imp_simp { + simp == imp_simp + } else { + false } + } else { + true } - ); + }); + + let impl_candidates = impl_iter.chain(nonblanket_iter) + .map(|imp| format!(" {}", imp)) + .take(5).collect::>(); if impl_candidates.len() > 0 { err.fileline_help( From 25e4389866d8a4483d7a20b5d3a72c2b00dd9c21 Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Mon, 28 Dec 2015 23:50:18 +0100 Subject: [PATCH 5/6] Use for_each_impl --- src/librustc/middle/traits/error_reporting.rs | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index f4235f3df21c6..adf0fdee18237 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -234,39 +234,30 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, if let Some(s) = custom_note { err.fileline_note(obligation.cause.span, &s); } else { - infcx.tcx.populate_implementations_for_trait_if_necessary( - trait_ref.def_id()); - + let simp = fast_reject::simplify_type(infcx.tcx, + trait_ref.self_ty(), + true); + let mut impl_candidates = Vec::new(); let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id()); - let blanket_impls = trait_def.blanket_impls.borrow(); - let impl_iter = blanket_impls.iter() - .filter_map(|&id| - infcx.tcx.impl_trait_ref(id)); - - let nonblanket = trait_def.nonblanket_impls.borrow(); - let nonblanket_iter = nonblanket.values() - .flat_map(|ids| - ids.iter().filter_map(|&id| - infcx.tcx.impl_trait_ref(id))); - - let simp = fast_reject::simplify_type(infcx.tcx, trait_ref.self_ty(), true); - let nonblanket_iter = nonblanket_iter.filter(|def| { + + trait_def.for_each_impl(infcx.tcx, |def_id| { + let imp = infcx.tcx.impl_trait_ref(def_id).unwrap(); if let Some(simp) = simp { - let imp_simp = fast_reject::simplify_type(infcx.tcx, def.self_ty(), true); + let imp_simp = fast_reject::simplify_type(infcx.tcx, + imp.self_ty(), + true); if let Some(imp_simp) = imp_simp { - simp == imp_simp + if simp == imp_simp { + impl_candidates.push(imp); + } } else { - false + impl_candidates.push(imp); } } else { - true + impl_candidates.push(imp); } }); - let impl_candidates = impl_iter.chain(nonblanket_iter) - .map(|imp| format!(" {}", imp)) - .take(5).collect::>(); - if impl_candidates.len() > 0 { err.fileline_help( obligation.cause.span, From a8d60708ecabc90ca4b72e12483159339a0b40ce Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Sun, 3 Jan 2016 00:11:48 +0100 Subject: [PATCH 6/6] Refactor candidate selection --- src/librustc/middle/traits/error_reporting.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index adf0fdee18237..038c414662a8a 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -240,23 +240,24 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, let mut impl_candidates = Vec::new(); let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id()); - trait_def.for_each_impl(infcx.tcx, |def_id| { - let imp = infcx.tcx.impl_trait_ref(def_id).unwrap(); - if let Some(simp) = simp { + match simp { + Some(simp) => trait_def.for_each_impl(infcx.tcx, |def_id| { + let imp = infcx.tcx.impl_trait_ref(def_id).unwrap(); let imp_simp = fast_reject::simplify_type(infcx.tcx, imp.self_ty(), true); if let Some(imp_simp) = imp_simp { - if simp == imp_simp { - impl_candidates.push(imp); + if simp != imp_simp { + return; } - } else { - impl_candidates.push(imp); } - } else { impl_candidates.push(imp); - } - }); + }), + None => trait_def.for_each_impl(infcx.tcx, |def_id| { + impl_candidates.push( + infcx.tcx.impl_trait_ref(def_id).unwrap()); + }) + }; if impl_candidates.len() > 0 { err.fileline_help( @@ -266,7 +267,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, let end = cmp::min(4, impl_candidates.len()); for candidate in &impl_candidates[0..end] { err.fileline_help(obligation.cause.span, - candidate); + &format!(" {:?}", candidate)); } if impl_candidates.len() > 4 { err.fileline_help(obligation.cause.span,