From 2e8e75f50f87dca154342790ba03401037a3c52e Mon Sep 17 00:00:00 2001 From: scalexm Date: Thu, 3 Aug 2017 13:50:06 +0200 Subject: [PATCH] Tweak error message --- src/librustc_typeck/check/method/confirm.rs | 8 ++- src/librustc_typeck/check/method/mod.rs | 25 +++---- src/librustc_typeck/check/method/suggest.rs | 71 +++++++++++++------- src/test/{compile-fail => ui}/issue-35976.rs | 6 +- src/test/ui/issue-35976.stderr | 11 +++ 5 files changed, 77 insertions(+), 44 deletions(-) rename src/test/{compile-fail => ui}/issue-35976.rs (67%) create mode 100644 src/test/ui/issue-35976.stderr diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 0ab75ad622ce..fd148062372f 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -110,8 +110,12 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { self.unify_receivers(self_ty, method_sig.inputs()[0]); // Add any trait/regions obligations specified on the method's type parameters. - let method_ty = self.tcx.mk_fn_ptr(ty::Binder(method_sig)); - self.add_obligations(method_ty, all_substs, &method_predicates); + // We won't add these if we encountered an illegal sized bound, so that we can use + // a custom error in that case. + if !rerun { + let method_ty = self.tcx.mk_fn_ptr(ty::Binder(method_sig)); + self.add_obligations(method_ty, all_substs, &method_predicates); + } // Create the final `MethodCallee`. let callee = MethodCallee { diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index f929a7ef49e1..eda17ab02c49 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -60,6 +60,10 @@ pub enum MethodError<'tcx> { // Found an applicable method, but it is not visible. PrivateMatch(Def), + + // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have + // forgotten to import a trait. + IllegalSizedBound(Vec), } // Contains a list of static methods that may apply, a list of unsatisfied trait predicates which @@ -112,6 +116,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Err(Ambiguity(..)) => true, Err(ClosureAmbiguity(..)) => true, Err(PrivateMatch(..)) => allow_private, + Err(IllegalSizedBound(..)) => true, } } @@ -173,13 +178,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self_ty, call_expr, ProbeScope::AllTraits) { + + // If we find a different result the caller probably forgot to import a trait. Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()], - Err(MethodError::Ambiguity(ref sources)) => { + Err(Ambiguity(ref sources)) => { sources.iter() .filter_map(|source| { match *source { // Note: this cannot come from an inherent impl, - // because the first probe succeeded. + // because the first probing succeeded. ImplSource(def) => self.tcx.trait_id_of_impl(def), TraitSource(_) => None, } @@ -189,19 +196,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => Vec::new(), }; - // If we find a different result, the caller probably forgot to import a trait. - // We span an error with an appropriate help message. - if !candidates.is_empty() { - let error = MethodError::NoMatch( - NoMatchData::new(Vec::new(), Vec::new(), candidates, probe::Mode::MethodCall) - ); - self.report_method_error(span, - self_ty, - segment.name, - Some(self_expr), - error, - None); - } + return Err(IllegalSizedBound(candidates)); } Ok(result.callee) diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 4faf71e0cc94..c480febdec66 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -315,9 +315,44 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let msg = format!("{} `{}` is private", def.kind_name(), item_name); self.tcx.sess.span_err(span, &msg); } + + MethodError::IllegalSizedBound(candidates) => { + let msg = format!("the `{}` method cannot be invoked on a trait object", item_name); + let mut err = self.sess().struct_span_err(span, &msg); + if !candidates.is_empty() { + let help = format!("{an}other candidate{s} {were} found in the following \ + trait{s}, perhaps add a `use` for {one_of_them}:", + an = if candidates.len() == 1 {"an" } else { "" }, + s = if candidates.len() == 1 { "" } else { "s" }, + were = if candidates.len() == 1 { "was" } else { "were" }, + one_of_them = if candidates.len() == 1 { + "it" + } else { + "one_of_them" + }); + self.suggest_use_candidates(&mut err, help, candidates); + } + err.emit(); + } } } + fn suggest_use_candidates(&self, + err: &mut DiagnosticBuilder, + mut msg: String, + candidates: Vec) { + let limit = if candidates.len() == 5 { 5 } else { 4 }; + for (i, trait_did) in candidates.iter().take(limit).enumerate() { + msg.push_str(&format!("\ncandidate #{}: `use {};`", + i + 1, + self.tcx.item_path_str(*trait_did))); + } + if candidates.len() > limit { + msg.push_str(&format!("\nand {} others", candidates.len() - limit)); + } + err.note(&msg[..]); + } + fn suggest_traits_to_import(&self, err: &mut DiagnosticBuilder, span: Span, @@ -330,30 +365,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { candidates.sort(); candidates.dedup(); err.help("items from traits can only be used if the trait is in scope"); - let mut msg = format!("the following {traits_are} implemented but not in scope, \ - perhaps add a `use` for {one_of_them}:", - traits_are = if candidates.len() == 1 { - "trait is" - } else { - "traits are" - }, - one_of_them = if candidates.len() == 1 { - "it" - } else { - "one of them" - }); - - let limit = if candidates.len() == 5 { 5 } else { 4 }; - for (i, trait_did) in candidates.iter().take(limit).enumerate() { - msg.push_str(&format!("\ncandidate #{}: `use {};`", - i + 1, - self.tcx.item_path_str(*trait_did))); - } - if candidates.len() > limit { - msg.push_str(&format!("\nand {} others", candidates.len() - limit)); - } - err.note(&msg[..]); + let msg = format!("the following {traits_are} implemented but not in scope, \ + perhaps add a `use` for {one_of_them}:", + traits_are = if candidates.len() == 1 { + "trait is" + } else { + "traits are" + }, + one_of_them = if candidates.len() == 1 { + "it" + } else { + "one of them" + }); + self.suggest_use_candidates(err, msg, candidates); return; } diff --git a/src/test/compile-fail/issue-35976.rs b/src/test/ui/issue-35976.rs similarity index 67% rename from src/test/compile-fail/issue-35976.rs rename to src/test/ui/issue-35976.rs index 194616c94437..169d7b559167 100644 --- a/src/test/compile-fail/issue-35976.rs +++ b/src/test/ui/issue-35976.rs @@ -22,10 +22,8 @@ mod private { fn bar(arg: Box) { arg.wait(); - //~^ ERROR no method named `wait` found for type `std::boxed::Box` - //~| the following trait is implemented but not in scope - //~| ERROR the trait bound `private::Future + 'static: std::marker::Sized` is not satisfied - //~| `private::Future + 'static` does not have a constant size known at compile-time + //~^ ERROR the `wait` method cannot be invoked on a trait object + //~| another candidate was found in the following trait, perhaps add a `use` for it: } fn main() { diff --git a/src/test/ui/issue-35976.stderr b/src/test/ui/issue-35976.stderr new file mode 100644 index 000000000000..9fb67449734b --- /dev/null +++ b/src/test/ui/issue-35976.stderr @@ -0,0 +1,11 @@ +error: the `wait` method cannot be invoked on a trait object + --> $DIR/issue-35976.rs:24:9 + | +24 | arg.wait(); + | ^^^^ + | + = note: another candidate was found in the following trait, perhaps add a `use` for it: + candidate #1: `use private::Future;` + +error: aborting due to previous error +