about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2024-12-31 18:06:01 +0000
committerEsteban Küber <esteban@kuber.com.ar>2024-12-31 18:06:01 +0000
commit2b2ea9e875dc9ae6c2d351078f1f43e533c9d780 (patch)
treedbbc1294b034869d46a03d36449b1ea29f44c9ea
parent7a0cde96f83c6d38237bb8062df6300ecf4c2687 (diff)
downloadrust-2b2ea9e875dc9ae6c2d351078f1f43e533c9d780.tar.gz
rust-2b2ea9e875dc9ae6c2d351078f1f43e533c9d780.zip
Provide structured suggestion for `impl Default` of type where all fields have defaults
```
error: `Default` impl doesn't use the declared default field values
  --> $DIR/manual-default-impl-could-be-derived.rs:28:1
   |
LL | / impl Default for B {
LL | |     fn default() -> Self {
LL | |         B {
LL | |             x: s(),
   | |                --- this field has a default value
LL | |             y: 0,
   | |                - this field has a default value
...  |
LL | | }
   | |_^
   |
help: to avoid divergence in behavior between `Struct { .. }` and `<Struct as Default>::default()`, derive the `Default`
   |
LL ~ #[derive(Default)] struct B {
   |
```

Note that above the structured suggestion also includes completely removing the manual `impl`, but the rendering doesn't.
-rw-r--r--compiler/rustc_lint/src/default_could_be_derived.rs34
-rw-r--r--tests/ui/structs/manual-default-impl-could-be-derived.stderr5
2 files changed, 30 insertions, 9 deletions
diff --git a/compiler/rustc_lint/src/default_could_be_derived.rs b/compiler/rustc_lint/src/default_could_be_derived.rs
index d95cbb05158..bae9defa687 100644
--- a/compiler/rustc_lint/src/default_could_be_derived.rs
+++ b/compiler/rustc_lint/src/default_could_be_derived.rs
@@ -1,9 +1,11 @@
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::Diag;
+use rustc_errors::{Applicability, Diag};
 use rustc_hir as hir;
 use rustc_middle::ty;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::Symbol;
+use rustc_span::def_id::DefId;
 use rustc_span::symbol::sym;
 
 use crate::{LateContext, LateLintPass};
@@ -149,13 +151,16 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
         let hir_id = cx.tcx.local_def_id_to_hir_id(local);
         let hir::Node::Item(item) = cx.tcx.hir_node(hir_id) else { return };
         cx.tcx.node_span_lint(DEFAULT_OVERRIDES_DEFAULT_FIELDS, hir_id, item.span, |diag| {
-            mk_lint(diag, orig_fields, fields);
+            mk_lint(cx.tcx, diag, type_def_id, parent, orig_fields, fields);
         });
     }
 }
 
 fn mk_lint(
+    tcx: TyCtxt<'_>,
     diag: &mut Diag<'_, ()>,
+    type_def_id: DefId,
+    impl_def_id: DefId,
     orig_fields: FxHashMap<Symbol, &hir::FieldDef<'_>>,
     fields: &[hir::ExprField<'_>],
 ) {
@@ -175,11 +180,24 @@ fn mk_lint(
         }
     }
 
-    diag.help(if removed_all_fields {
-        "to avoid divergence in behavior between `Struct { .. }` and \
-         `<Struct as Default>::default()`, derive the `Default`"
+    if removed_all_fields {
+        let msg = "to avoid divergence in behavior between `Struct { .. }` and \
+                   `<Struct as Default>::default()`, derive the `Default`";
+        if let Some(hir::Node::Item(impl_)) = tcx.hir().get_if_local(impl_def_id) {
+            diag.multipart_suggestion_verbose(
+                msg,
+                vec![
+                    (tcx.def_span(type_def_id).shrink_to_lo(), "#[derive(Default)] ".to_string()),
+                    (impl_.span, String::new()),
+                ],
+                Applicability::MachineApplicable,
+            );
+        } else {
+            diag.help(msg);
+        }
     } else {
-        "use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them \
-         diverging over time"
-    });
+        let msg = "use the default values in the `impl` with `Struct { mandatory_field, .. }` to \
+                   avoid them diverging over time";
+        diag.help(msg);
+    }
 }
diff --git a/tests/ui/structs/manual-default-impl-could-be-derived.stderr b/tests/ui/structs/manual-default-impl-could-be-derived.stderr
index e8f607fac7e..cf06d5418e1 100644
--- a/tests/ui/structs/manual-default-impl-could-be-derived.stderr
+++ b/tests/ui/structs/manual-default-impl-could-be-derived.stderr
@@ -31,7 +31,10 @@ LL | |             y: 0,
 LL | | }
    | |_^
    |
-   = help: to avoid divergence in behavior between `Struct { .. }` and `<Struct as Default>::default()`, derive the `Default`
+help: to avoid divergence in behavior between `Struct { .. }` and `<Struct as Default>::default()`, derive the `Default`
+   |
+LL ~ #[derive(Default)] struct B {
+   |
 
 error: `Default` impl doesn't use the declared default field values
   --> $DIR/manual-default-impl-could-be-derived.rs:43:1