]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/borrow_check/conflict_errors.rs
Use structured suggestion when requiring `Copy` constraint in type param
[rust.git] / src / librustc_mir / borrow_check / conflict_errors.rs
index 9364bbedb0c587c2cd8d994e24123d3da5eaae18..e3da090a62d30f17e091fa866c1f03ed5c29a191 100644 (file)
@@ -231,12 +231,64 @@ pub(super) fn report_use_of_moved_or_uninitialized(
                 if let ty::Param(param_ty) = ty.kind {
                     let tcx = self.infcx.tcx;
                     let generics = tcx.generics_of(self.mir_def_id);
-                    let def_id = generics.type_param(&param_ty, tcx).def_id;
-                    if let Some(sp) = tcx.hir().span_if_local(def_id) {
-                        err.span_label(
-                            sp,
-                            "consider adding a `Copy` constraint to this type argument",
-                        );
+                    let param = generics.type_param(&param_ty, tcx);
+                    let generics = tcx.hir().get_generics(self.mir_def_id).unwrap();
+                    let msg = "consider adding a `Copy` constraint to this type argument";
+                    for param in generics.params.iter().filter(|p| {
+                        p.name.ident().as_str() == param.name.as_str()
+                    }) {
+                        let param_name = param.name.ident().as_str();
+                        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,
+                                msg,
+                                // `impl CurrentTrait + MissingTrait`
+                                format!("{} + Copy", param_name),
+                                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,
+                                msg,
+                                format!("{}: Copy", param_name),
+                                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(),
+                                msg,
+                                format!(", {}: Copy", param_name),
+                                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 = 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, msg, format!(
+                                    "{}: Copy +",
+                                    param_name,
+                                ), Applicability::MachineApplicable);
+                            } else {
+                                err.span_label(param.span, msg);
+                            }
+                        }
                     }
                 }
                 let span = if let Some(local) = place.as_local() {
@@ -308,7 +360,14 @@ pub(super) fn report_move_out_while_borrowed(
             location,
             borrow,
             None,
-        ).add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", Some(borrow_span));
+        ).add_explanation_to_diagnostic(
+            self.infcx.tcx,
+            self.body,
+            &self.local_names,
+            &mut err,
+            "",
+            Some(borrow_span),
+        );
         err.buffer(&mut self.errors_buffer);
     }
 
@@ -343,7 +402,14 @@ pub(super) fn report_use_while_mutably_borrowed(
         });
 
         self.explain_why_borrow_contains_point(location, borrow, None)
-            .add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
+            .add_explanation_to_diagnostic(
+                self.infcx.tcx,
+                self.body,
+                &self.local_names,
+                &mut err,
+                "",
+                None,
+            );
         err
     }
 
@@ -561,6 +627,7 @@ pub(super) fn report_conflicting_borrow(
         explanation.add_explanation_to_diagnostic(
             self.infcx.tcx,
             self.body,
+            &self.local_names,
             &mut err,
             first_borrow_desc,
             None,
@@ -947,6 +1014,7 @@ fn report_local_value_does_not_live_long_enough(
                 explanation.add_explanation_to_diagnostic(
                     self.infcx.tcx,
                     self.body,
+                    &self.local_names,
                     &mut err,
                     "",
                     None,
@@ -971,7 +1039,7 @@ fn report_local_value_does_not_live_long_enough(
             );
 
             explanation.add_explanation_to_diagnostic(
-                self.infcx.tcx, self.body, &mut err, "", None);
+                self.infcx.tcx, self.body, &self.local_names, &mut err, "", None);
         }
 
         err
@@ -1029,7 +1097,14 @@ fn report_borrow_conflicts_with_destructor(
             _ => {}
         }
 
-        explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
+        explanation.add_explanation_to_diagnostic(
+            self.infcx.tcx,
+            self.body,
+            &self.local_names,
+            &mut err,
+            "",
+            None,
+        );
 
         err.buffer(&mut self.errors_buffer);
     }
@@ -1109,7 +1184,14 @@ fn report_temporary_value_does_not_live_long_enough(
             }
             _ => {}
         }
-        explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
+        explanation.add_explanation_to_diagnostic(
+            self.infcx.tcx,
+            self.body,
+            &self.local_names,
+            &mut err,
+            "",
+            None,
+        );
 
         let within = if borrow_spans.for_generator() {
             " by generator"
@@ -1478,7 +1560,14 @@ pub(super) fn report_illegal_mutation_of_borrowed(
         );
 
         self.explain_why_borrow_contains_point(location, loan, None)
-            .add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
+            .add_explanation_to_diagnostic(
+                self.infcx.tcx,
+                self.body,
+                &self.local_names,
+                &mut err,
+                "",
+                None,
+            );
 
         err.buffer(&mut self.errors_buffer);
     }
@@ -1496,14 +1585,13 @@ pub(super) fn report_illegal_reassignment(
         assigned_span: Span,
         err_place: &Place<'tcx>,
     ) {
-        let (from_arg, local_decl) = if let Some(local) = err_place.as_local() {
-            if let LocalKind::Arg = self.body.local_kind(local) {
-                (true, Some(&self.body.local_decls[local]))
-            } else {
-                (false, Some(&self.body.local_decls[local]))
-            }
-        } else {
-            (false, None)
+        let (from_arg, local_decl, local_name) = match err_place.as_local() {
+            Some(local) => (
+                self.body.local_kind(local) == LocalKind::Arg,
+                Some(&self.body.local_decls[local]),
+                self.local_names[local],
+            ),
+            None => (false, None, None),
         };
 
         // If root local is initialized immediately (everything apart from let
@@ -1553,7 +1641,7 @@ pub(super) fn report_illegal_reassignment(
             }
         }
         if let Some(decl) = local_decl {
-            if let Some(name) = decl.name {
+            if let Some(name) = local_name {
                 if decl.can_be_made_mutable() {
                     err.span_suggestion(
                         decl.source_info.span,