]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/traits/error_reporting.rs
Auto merge of #65456 - estebank:trait-bound-borrow, r=matthewjasper
[rust.git] / src / librustc / traits / error_reporting.rs
index fe18a14d890586c17595a823ef4da7b7db195f7a..ea29cc0d93f538a51c8f38375e24fde6a6bb4783 100644 (file)
 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_error_codes::*;
+
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub fn report_fulfillment_errors(
         &self,
@@ -383,9 +385,9 @@ fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
         let hir = &self.tcx.hir();
         let node = hir.find(hir_id)?;
         if let hir::Node::Item(
-            hir::Item{kind: hir::ItemKind::Fn(_ ,fn_header ,_ , body_id), .. }) = &node {
+            hir::Item{kind: hir::ItemKind::Fn(sig, _, body_id), .. }) = &node {
             self.describe_generator(*body_id).or_else(||
-                Some(if let hir::FnHeader{ asyncness: hir::IsAsync::Async, .. } = fn_header {
+                Some(if let hir::FnHeader{ asyncness: hir::IsAsync::Async, .. } = sig.header {
                     "an async function"
                 } else {
                     "a function"
@@ -711,20 +713,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 == "?")
@@ -765,6 +771,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!
@@ -1081,7 +1098,7 @@ fn suggest_restricting_param_bound(
                 }
 
                 hir::Node::Item(hir::Item {
-                    kind: hir::ItemKind::Fn(_, _, generics, _), ..
+                    kind: hir::ItemKind::Fn(_, generics, _), ..
                 }) |
                 hir::Node::TraitItem(hir::TraitItem {
                     generics,
@@ -1112,7 +1129,7 @@ fn suggest_restricting_param_bound(
                     kind: hir::ItemKind::Impl(_, _, _, generics, ..), span, ..
                 }) |
                 hir::Node::Item(hir::Item {
-                    kind: hir::ItemKind::Fn(_, _, generics, _), span, ..
+                    kind: hir::ItemKind::Fn(_, generics, _), span, ..
                 }) |
                 hir::Node::Item(hir::Item {
                     kind: hir::ItemKind::TyAlias(_, generics), span, ..
@@ -1296,6 +1313,73 @@ fn suggest_fn_call(
         }
     }
 
+    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(
@@ -1385,8 +1469,8 @@ fn suggest_change_mut(
 
             if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind {
                 let trait_type = match mutability {
-                    hir::Mutability::MutMutable => self.tcx.mk_imm_ref(region, t_type),
-                    hir::Mutability::MutImmutable => self.tcx.mk_mut_ref(region, t_type),
+                    hir::Mutability::Mutable => self.tcx.mk_imm_ref(region, t_type),
+                    hir::Mutability::Immutable => self.tcx.mk_mut_ref(region, t_type),
                 };
 
                 let substs = self.tcx.mk_substs_trait(&trait_type, &[]);
@@ -1403,7 +1487,7 @@ fn suggest_change_mut(
                     let sp = self.tcx.sess.source_map()
                         .span_take_while(span, |c| c.is_whitespace() || *c == '&');
                     if points_at_arg &&
-                        mutability == hir::Mutability::MutImmutable &&
+                        mutability == hir::Mutability::Immutable &&
                         refs_number > 0
                     {
                         err.span_suggestion(
@@ -1436,12 +1520,12 @@ fn suggest_semicolon_removal(
         let parent_node = hir.get_parent_node(obligation.cause.body_id);
         let node = hir.find(parent_node);
         if let Some(hir::Node::Item(hir::Item {
-            kind: hir::ItemKind::Fn(decl, _, _, body_id),
+            kind: hir::ItemKind::Fn(sig, _, body_id),
             ..
         })) = node {
             let body = hir.body(*body_id);
             if let hir::ExprKind::Block(blk, _) = &body.value.kind {
-                if decl.output.span().overlaps(span) && blk.expr.is_none() &&
+                if sig.decl.output.span().overlaps(span) && blk.expr.is_none() &&
                     "()" == &trait_ref.self_ty().to_string()
                 {
                     // FIXME(estebank): When encountering a method with a trait
@@ -1493,20 +1577,20 @@ pub fn get_fn_like_arguments(&self, node: Node<'_>) -> (Span, Vec<ArgKind>) {
             }
             Node::Item(&hir::Item {
                 span,
-                kind: hir::ItemKind::Fn(ref decl, ..),
+                kind: hir::ItemKind::Fn(ref sig, ..),
                 ..
             }) |
             Node::ImplItem(&hir::ImplItem {
                 span,
-                kind: hir::ImplItemKind::Method(hir::MethodSig { ref decl, .. }, _),
+                kind: hir::ImplItemKind::Method(ref sig, _),
                 ..
             }) |
             Node::TraitItem(&hir::TraitItem {
                 span,
-                kind: hir::TraitItemKind::Method(hir::MethodSig { ref decl, .. }, _),
+                kind: hir::TraitItemKind::Method(ref sig, _),
                 ..
             }) => {
-                (self.tcx.sess.source_map().def_span(span), decl.inputs.iter()
+                (self.tcx.sess.source_map().def_span(span), sig.decl.inputs.iter()
                         .map(|arg| match arg.clone().kind {
                     hir::TyKind::Tup(ref tys) => ArgKind::Tuple(
                         Some(arg.span),
@@ -2040,11 +2124,11 @@ fn note_obligation_cause_for_async_await(
             .and_then(|parent_did| self.tcx.hir().get_if_local(parent_did));
         debug!("note_obligation_cause_for_async_await: parent_node={:?}", parent_node);
         if let Some(hir::Node::Item(hir::Item {
-            kind: hir::ItemKind::Fn(_, header, _, _),
+            kind: hir::ItemKind::Fn(sig, _, _),
             ..
         })) = parent_node {
-            debug!("note_obligation_cause_for_async_await: header={:?}", header);
-            if header.asyncness != hir::IsAsync::Async {
+            debug!("note_obligation_cause_for_async_await: header={:?}", sig.header);
+            if sig.header.asyncness != hir::IsAsync::Async {
                 return false;
             }
         }
@@ -2174,15 +2258,15 @@ fn note_obligation_cause_code<T>(&self,
                 err.note(&format!("required by cast to type `{}`",
                                   self.ty_to_string(target)));
             }
-            ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expression) => {
+            ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expressions) => {
                 err.note("the `Copy` trait is required because the \
                           repeated element will be copied");
-                if suggest_const_in_array_repeat_expression {
+                if suggest_const_in_array_repeat_expressions {
                     err.note("this array initializer can be evaluated at compile-time, for more \
                               information, see issue \
                               https://github.com/rust-lang/rust/issues/49147");
                     if tcx.sess.opts.unstable_features.is_nightly_build() {
-                        err.help("add `#![feature(const_in_array_repeat_expression)]` to the \
+                        err.help("add `#![feature(const_in_array_repeat_expressions)]` to the \
                                   crate attributes to enable");
                     }
                 }
@@ -2287,11 +2371,14 @@ fn note_obligation_cause_code<T>(&self,
                     );
                 }
             }
-            ObligationCauseCode::AssocTypeBound(impl_span, orig) => {
-                err.span_label(orig, "associated type defined here");
-                if let Some(sp) = impl_span {
+            ObligationCauseCode::AssocTypeBound(ref data) => {
+                err.span_label(data.original, "associated type defined here");
+                if let Some(sp) = data.impl_span {
                     err.span_label(sp, "in this `impl` item");
                 }
+                for sp in &data.bounds {
+                    err.span_label(*sp, "restricted in this bound");
+                }
             }
         }
     }