]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
Rollup merge of #107769 - compiler-errors:pointer-like, r=eholk
[rust.git] / compiler / rustc_infer / src / infer / error_reporting / note_and_explain.rs
index 425cde3302db8b64aa8e5d1bc391e06025278dc0..984e8cf6a0eb909f872fb50574b53c586847a349 100644 (file)
@@ -77,49 +77,86 @@ pub fn note_and_explain_type_err(
                     (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
                         if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
                     {
-                        let generics = tcx.generics_of(body_owner_def_id);
-                        let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+                        let p_def_id = tcx
+                            .generics_of(body_owner_def_id)
+                            .type_param(p, tcx)
+                            .def_id;
+                        let p_span = tcx.def_span(p_def_id);
                         if !sp.contains(p_span) {
                             diag.span_label(p_span, "this type parameter");
                         }
                         let hir = tcx.hir();
                         let mut note = true;
-                        if let Some(generics) = generics
-                            .type_param(p, tcx)
-                            .def_id
+                        let parent = p_def_id
                             .as_local()
-                            .map(|id| hir.local_def_id_to_hir_id(id))
-                            .and_then(|id| tcx.hir().find_parent(id))
-                            .as_ref()
-                            .and_then(|node| node.generics())
+                            .and_then(|id| {
+                                let local_id = hir.local_def_id_to_hir_id(id);
+                                let generics = tcx.hir().find_parent(local_id)?.generics()?;
+                                Some((id, generics))
+                            });
+                        if let Some((local_id, generics)) = parent
                         {
                             // Synthesize the associated type restriction `Add<Output = Expected>`.
                             // FIXME: extract this logic for use in other diagnostics.
                             let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx);
-                            let path =
-                                tcx.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
                             let item_name = tcx.item_name(proj.def_id);
                             let item_args = self.format_generic_args(assoc_substs);
 
-                            let path = if path.ends_with('>') {
-                                format!(
-                                    "{}, {}{} = {}>",
-                                    &path[..path.len() - 1],
-                                    item_name,
-                                    item_args,
-                                    p
-                                )
+                            // Here, we try to see if there's an existing
+                            // trait implementation that matches the one that
+                            // we're suggesting to restrict. If so, find the
+                            // "end", whether it be at the end of the trait
+                            // or the end of the generic arguments.
+                            let mut matching_span = None;
+                            let mut matched_end_of_args = false;
+                            for bound in generics.bounds_for_param(local_id) {
+                                let potential_spans = bound
+                                    .bounds
+                                    .iter()
+                                    .find_map(|bound| {
+                                        let bound_trait_path = bound.trait_ref()?.path;
+                                        let def_id = bound_trait_path.res.opt_def_id()?;
+                                        let generic_args = bound_trait_path.segments.iter().last().map(|path| path.args());
+                                        (def_id == trait_ref.def_id).then_some((bound_trait_path.span, generic_args))
+                                    });
+
+                                if let Some((end_of_trait, end_of_args)) = potential_spans {
+                                    let args_span = end_of_args.and_then(|args| args.span());
+                                    matched_end_of_args = args_span.is_some();
+                                    matching_span = args_span
+                                        .or_else(|| Some(end_of_trait))
+                                        .map(|span| span.shrink_to_hi());
+                                    break;
+                                }
+                            }
+
+                            if matched_end_of_args {
+                                // Append suggestion to the end of our args
+                                let path = format!(", {}{} = {}",item_name, item_args, p);
+                                note = !suggest_constraining_type_param(
+                                    tcx,
+                                    generics,
+                                    diag,
+                                    &format!("{}", proj.self_ty()),
+                                    &path,
+                                    None,
+                                    matching_span,
+                                );
                             } else {
-                                format!("{}<{}{} = {}>", path, item_name, item_args, p)
-                            };
-                            note = !suggest_constraining_type_param(
-                                tcx,
-                                generics,
-                                diag,
-                                &format!("{}", proj.self_ty()),
-                                &path,
-                                None,
-                            );
+                                // Suggest adding a bound to an existing trait
+                                // or if the trait doesn't exist, add the trait
+                                // and the suggested bounds.
+                                let path = format!("<{}{} = {}>", item_name, item_args, p);
+                                note = !suggest_constraining_type_param(
+                                    tcx,
+                                    generics,
+                                    diag,
+                                    &format!("{}", proj.self_ty()),
+                                    &path,
+                                    None,
+                                    matching_span,
+                                );
+                            }
                         }
                         if note {
                             diag.note("you might be missing a type parameter or trait bound");
@@ -137,25 +174,25 @@ pub fn note_and_explain_type_err(
                             diag.help(
                                 "given a type parameter `T` and a method `foo`:
 ```
-trait Trait<T> { fn foo(&tcx) -> T; }
+trait Trait<T> { fn foo(&self) -> T; }
 ```
 the only ways to implement method `foo` are:
 - constrain `T` with an explicit type:
 ```
 impl Trait<String> for X {
-    fn foo(&tcx) -> String { String::new() }
+    fn foo(&self) -> String { String::new() }
 }
 ```
 - add a trait bound to `T` and call a method on that trait that returns `Self`:
 ```
 impl<T: std::default::Default> Trait<T> for X {
-    fn foo(&tcx) -> T { <T as std::default::Default>::default() }
+    fn foo(&self) -> T { <T as std::default::Default>::default() }
 }
 ```
 - change `foo` to return an argument of type `T`:
 ```
 impl<T> Trait<T> for X {
-    fn foo(&tcx, x: T) -> T { x }
+    fn foo(&self, x: T) -> T { x }
 }
 ```",
                             );
@@ -218,6 +255,13 @@ fn foo(&tcx, x: T) -> T { x }
                             );
                         }
                     }
+                    (ty::FnPtr(_), ty::FnDef(def, _))
+                    if let hir::def::DefKind::Fn = tcx.def_kind(def) => {
+                        diag.note(
+                            "when the arguments and return types match, functions can be coerced \
+                             to function pointers",
+                        );
+                    }
                     _ => {}
                 }
                 debug!(
@@ -389,14 +433,14 @@ fn expected_projection(
 ```
 trait Trait {
 type T;
-fn foo(&tcx) -> Self::T;
+fn foo(&self) -> Self::T;
 }
 ```
 the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
 ```
 impl Trait for X {
 type T = String;
-fn foo(&tcx) -> Self::T { String::new() }
+fn foo(&self) -> Self::T { String::new() }
 }
 ```",
             );
@@ -463,7 +507,7 @@ fn point_at_methods_that_satisfy_associated_type(
                 ty::AssocKind::Fn == item.kind && Some(item.name) != current_method_ident
             })
             .filter_map(|item| {
-                let method = tcx.fn_sig(item.def_id);
+                let method = tcx.fn_sig(item.def_id).subst_identity();
                 match *method.output().skip_binder().kind() {
                     ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
                         if item_def_id == proj_ty_item_def_id =>