X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc%2Ftraits%2Ferror_reporting.rs;h=65d08ab03aaaf699bd60872ff6d53a5ec4198609;hb=48b0fd2060614fc917616e5023a2b1af3158308a;hp=18f083e154a86eb523d22eefb95563452e9a76ad;hpb=614da98454984921142eb2059db7be953d2c855c;p=rust.git diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 18f083e154a..65d08ab03aa 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -33,11 +33,12 @@ use crate::ty::SubtypePredicate; use crate::util::nodemap::{FxHashMap, FxHashSet}; -use errors::{Applicability, DiagnosticBuilder, pluralize}; +use errors::{Applicability, DiagnosticBuilder, pluralize, Style}; use std::fmt; use syntax::ast; use syntax::symbol::{sym, kw}; use syntax_pos::{DUMMY_SP, Span, ExpnKind, MultiSpan}; +use rustc::hir::def_id::LOCAL_CRATE; use rustc_error_codes::*; @@ -165,7 +166,7 @@ fn report_fulfillment_error( body_id: Option, fallback_has_occurred: bool, ) { - debug!("report_fulfillment_errors({:?})", error); + debug!("report_fulfillment_error({:?})", error); match error.code { FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { self.report_selection_error( @@ -713,20 +714,24 @@ pub fn report_selection_error( } match obligation.predicate { ty::Predicate::Trait(ref trait_predicate) => { - let trait_predicate = - self.resolve_vars_if_possible(trait_predicate); + let trait_predicate = self.resolve_vars_if_possible(trait_predicate); if self.tcx.sess.has_errors() && trait_predicate.references_error() { return; } let trait_ref = trait_predicate.to_poly_trait_ref(); - let (post_message, pre_message) = - self.get_parent_trait_ref(&obligation.cause.code) - .map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t))) + let ( + post_message, + pre_message, + ) = self.get_parent_trait_ref(&obligation.cause.code) + .map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t))) .unwrap_or_default(); - let OnUnimplementedNote { message, label, note } - = self.on_unimplemented_note(trait_ref, obligation); + let OnUnimplementedNote { + message, + label, + note, + } = self.on_unimplemented_note(trait_ref, obligation); let have_alt_message = message.is_some() || label.is_some(); let is_try = self.tcx.sess.source_map().span_to_snippet(span) .map(|s| &s == "?") @@ -767,6 +772,17 @@ pub fn report_selection_error( ) }; + if self.suggest_add_reference_to_arg( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + have_alt_message, + ) { + self.note_obligation_cause(&mut err, obligation); + err.emit(); + return; + } if let Some(ref s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! @@ -784,6 +800,7 @@ pub fn report_selection_error( self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); self.suggest_remove_reference(&obligation, &mut err, &trait_ref); self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); + self.note_version_mismatch(&mut err, &trait_ref); // Try to report a help message if !trait_ref.has_infer_types() && @@ -1035,6 +1052,43 @@ pub fn report_selection_error( err.emit(); } + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) { + let get_trait_impl = |trait_def_id| { + let mut trait_impl = None; + self.tcx.for_each_relevant_impl(trait_def_id, trait_ref.self_ty(), |impl_def_id| { + if trait_impl.is_none() { + trait_impl = Some(impl_def_id); + } + }); + trait_impl + }; + let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); + let all_traits = self.tcx.all_traits(LOCAL_CRATE); + let traits_with_same_path: std::collections::BTreeSet<_> = all_traits + .iter() + .filter(|trait_def_id| **trait_def_id != trait_ref.def_id()) + .filter(|trait_def_id| self.tcx.def_path_str(**trait_def_id) == required_trait_path) + .collect(); + for trait_with_same_path in traits_with_same_path { + if let Some(impl_def_id) = get_trait_impl(*trait_with_same_path) { + let impl_span = self.tcx.def_span(impl_def_id); + err.span_help(impl_span, "trait impl with same name found"); + let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); + let crate_msg = format!( + "Perhaps two different versions of crate `{}` are being used?", + trait_crate + ); + err.note(&crate_msg); + } + } + } fn suggest_restricting_param_bound( &self, err: &mut DiagnosticBuilder<'_>, @@ -1371,6 +1425,73 @@ fn suggest_fn_call( } } + fn suggest_add_reference_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::Binder>, + points_at_arg: bool, + has_custom_message: bool, + ) -> bool { + if !points_at_arg { + return false; + } + + let span = obligation.cause.span; + let param_env = obligation.param_env; + let trait_ref = trait_ref.skip_binder(); + + if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code { + // Try to apply the original trait binding obligation by borrowing. + let self_ty = trait_ref.self_ty(); + let found = self_ty.to_string(); + let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty); + let substs = self.tcx.mk_substs_trait(new_self_ty, &[]); + let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs); + let new_obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + new_trait_ref.to_predicate(), + ); + if self.predicate_must_hold_modulo_regions(&new_obligation) { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + // We have a very specific type of error, where just borrowing this argument + // might solve the problem. In cases like this, the important part is the + // original type obligation, not the last one that failed, which is arbitrary. + // Because of this, we modify the error to refer to the original obligation and + // return early in the caller. + let msg = format!( + "the trait bound `{}: {}` is not satisfied", + found, + obligation.parent_trait_ref.skip_binder(), + ); + if has_custom_message { + err.note(&msg); + } else { + err.message = vec![(msg, Style::NoStyle)]; + } + if snippet.starts_with('&') { + // This is already a literal borrow and the obligation is failing + // somewhere else in the obligation chain. Do not suggest non-sense. + return false; + } + err.span_label(span, &format!( + "expected an implementor of trait `{}`", + obligation.parent_trait_ref.skip_binder(), + )); + err.span_suggestion( + span, + "consider borrowing here", + format!("&{}", snippet), + Applicability::MaybeIncorrect, + ); + return true; + } + } + } + false + } + /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, /// suggest removing these references until we reach a type that implements the trait. fn suggest_remove_reference(