]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/traits/error_reporting.rs
review comments
[rust.git] / src / librustc / traits / error_reporting.rs
index 0144d51a9693f82c2d422b0aeef6f86dde4824df..6e723cdc999b02a56c64688a5644d9d475d2a6f0 100644 (file)
@@ -38,6 +38,8 @@
 use syntax::ast;
 use syntax::symbol::{sym, kw};
 use syntax_pos::{DUMMY_SP, Span, ExpnKind, MultiSpan};
+use rustc::hir::def_id::LOCAL_CRATE;
+use syntax_pos::source_map::SourceMap;
 
 use rustc_error_codes::*;
 
@@ -165,7 +167,7 @@ fn report_fulfillment_error(
         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(
@@ -799,6 +801,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() &&
@@ -1050,9 +1053,46 @@ pub fn report_selection_error(
         err.emit();
     }
 
-    fn suggest_restricting_param_bound(
+    /// 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,
+        mut err: &mut DiagnosticBuilder<'_>,
         trait_ref: &ty::PolyTraitRef<'_>,
         body_id: hir::HirId,
     ) {
@@ -1063,7 +1103,11 @@ fn suggest_restricting_param_bound(
             _ => return,
         };
 
-        let mut suggest_restriction = |generics: &hir::Generics, msg| {
+        let suggest_restriction = |
+            generics: &hir::Generics,
+            msg,
+            err: &mut DiagnosticBuilder<'_>,
+        | {
             let span = generics.where_clause.span_for_predicates_or_empty_place();
             if !span.from_expansion() && span.desugaring_kind().is_none() {
                 err.span_suggestion(
@@ -1093,7 +1137,7 @@ fn suggest_restricting_param_bound(
                     kind: hir::TraitItemKind::Method(..), ..
                 }) if param_ty && self_ty == self.tcx.types.self_param => {
                     // Restricting `Self` for a single method.
-                    suggest_restriction(&generics, "`Self`");
+                    suggest_restriction(&generics, "`Self`", err);
                     return;
                 }
 
@@ -1115,7 +1159,7 @@ fn suggest_restricting_param_bound(
                     kind: hir::ItemKind::Impl(_, _, _, generics, ..), ..
                 }) if projection.is_some() => {
                     // Missing associated type bound.
-                    suggest_restriction(&generics, "the associated type");
+                    suggest_restriction(&generics, "the associated type", err);
                     return;
                 }
 
@@ -1144,68 +1188,16 @@ fn suggest_restricting_param_bound(
                 hir::Node::ImplItem(hir::ImplItem { generics, span, .. })
                 if param_ty => {
                     // Missing generic type parameter bound.
-                    let restrict_msg = "consider further restricting this bound";
                     let param_name = self_ty.to_string();
-                    for param in generics.params.iter().filter(|p| {
-                        p.name.ident().as_str() == param_name
-                    }) {
-                        if param_name.starts_with("impl ") {
-                            // `impl Trait` in argument:
-                            // `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
-                            err.span_suggestion(
-                                param.span,
-                                restrict_msg,
-                                // `impl CurrentTrait + MissingTrait`
-                                format!("{} + {}", param.name.ident(), trait_ref),
-                                Applicability::MachineApplicable,
-                            );
-                        } else if generics.where_clause.predicates.is_empty() &&
-                                param.bounds.is_empty()
-                        {
-                            // If there are no bounds whatsoever, suggest adding a constraint
-                            // to the type parameter:
-                            // `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
-                            err.span_suggestion(
-                                param.span,
-                                "consider restricting this bound",
-                                format!("{}", trait_ref.to_predicate()),
-                                Applicability::MachineApplicable,
-                            );
-                        } else if !generics.where_clause.predicates.is_empty() {
-                            // There is a `where` clause, so suggest expanding it:
-                            // `fn foo<T>(t: T) where T: Debug {}` →
-                            // `fn foo<T>(t: T) where T: Debug, T: Trait {}`
-                            err.span_suggestion(
-                                generics.where_clause.span().unwrap().shrink_to_hi(),
-                                &format!(
-                                    "consider further restricting type parameter `{}`",
-                                    param_name,
-                                ),
-                                format!(", {}", trait_ref.to_predicate()),
-                                Applicability::MachineApplicable,
-                            );
-                        } else {
-                            // If there is no `where` clause lean towards constraining to the
-                            // type parameter:
-                            // `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
-                            // `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
-                            let sp = param.span.with_hi(span.hi());
-                            let span = self.tcx.sess.source_map()
-                                .span_through_char(sp, ':');
-                            if sp != param.span && sp != span {
-                                // Only suggest if we have high certainty that the span
-                                // covers the colon in `foo<T: Trait>`.
-                                err.span_suggestion(span, restrict_msg, format!(
-                                    "{} + ",
-                                    trait_ref.to_predicate(),
-                                ), Applicability::MachineApplicable);
-                            } else {
-                                err.span_label(param.span, &format!(
-                                    "consider adding a `where {}` bound",
-                                    trait_ref.to_predicate(),
-                                ));
-                            }
-                        }
+                    let constraint = trait_ref.to_string();
+                    if suggest_constraining_type_param(
+                        generics,
+                        &mut err,
+                        &param_name,
+                        &constraint,
+                        self.tcx.sess.source_map(),
+                        *span,
+                    ) {
                         return;
                     }
                 }
@@ -2507,3 +2499,76 @@ pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind {
         }
     }
 }
+
+/// Suggest restricting a type param with a new bound.
+pub fn suggest_constraining_type_param(
+    generics: &hir::Generics,
+    err: &mut DiagnosticBuilder<'_>,
+    param_name: &str,
+    constraint: &str,
+    source_map: &SourceMap,
+    span: Span,
+) -> bool {
+    let restrict_msg = "consider further restricting this bound";
+    if let Some(param) = generics.params.iter().filter(|p| {
+        p.name.ident().as_str() == param_name
+    }).next() {
+        if param_name.starts_with("impl ") {
+            // `impl Trait` in argument:
+            // `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
+            err.span_suggestion(
+                param.span,
+                restrict_msg,
+                // `impl CurrentTrait + MissingTrait`
+                format!("{} + {}", param_name, constraint),
+                Applicability::MachineApplicable,
+            );
+        } else if generics.where_clause.predicates.is_empty() &&
+                param.bounds.is_empty()
+        {
+            // If there are no bounds whatsoever, suggest adding a constraint
+            // to the type parameter:
+            // `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
+            err.span_suggestion(
+                param.span,
+                "consider restricting this bound",
+                format!("{}: {}", param_name, constraint),
+                Applicability::MachineApplicable,
+            );
+        } else if !generics.where_clause.predicates.is_empty() {
+            // There is a `where` clause, so suggest expanding it:
+            // `fn foo<T>(t: T) where T: Debug {}` →
+            // `fn foo<T>(t: T) where T: Debug, T: Trait {}`
+            err.span_suggestion(
+                generics.where_clause.span().unwrap().shrink_to_hi(),
+                &format!("consider further restricting type parameter `{}`", param_name),
+                format!(", {}: {}", param_name, constraint),
+                Applicability::MachineApplicable,
+            );
+        } else {
+            // If there is no `where` clause lean towards constraining to the
+            // type parameter:
+            // `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
+            // `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
+            let sp = param.span.with_hi(span.hi());
+            let span = source_map.span_through_char(sp, ':');
+            if sp != param.span && sp != span {
+                // Only suggest if we have high certainty that the span
+                // covers the colon in `foo<T: Trait>`.
+                err.span_suggestion(
+                    span,
+                    restrict_msg,
+                    format!("{}: {} + ", param_name, constraint),
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                err.span_label(
+                    param.span,
+                    &format!("consider adding a `where {}: {}` bound", param_name, constraint),
+                );
+            }
+        }
+        return true;
+    }
+    false
+}