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::*;
body_id: Option<hir::BodyId>,
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(
}
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 == "?")
)
};
+ 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!
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() &&
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<'_>,
}
}
+ fn suggest_add_reference_to_arg(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ err: &mut DiagnosticBuilder<'tcx>,
+ trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
+ 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(