about summary refs log tree commit diff
diff options
context:
space:
mode:
authorxizheyin <xizheyin@smail.nju.edu.cn>2025-03-27 17:28:15 +0800
committerxizheyin <xizheyin@smail.nju.edu.cn>2025-03-27 20:09:37 +0800
commit4648650d899ba01a6ed30e7ada3795b5c56465eb (patch)
treed79ac25536661d513b99c5df3e44ad640c3bcca7
parentd0353f5c7ac0c73c6ae97623ac3e01fab1f657ae (diff)
downloadrust-4648650d899ba01a6ed30e7ada3795b5c56465eb.tar.gz
rust-4648650d899ba01a6ed30e7ada3795b5c56465eb.zip
Improve suggest construct with literal syntax instead of calling
Signed-off-by: xizheyin <xizheyin@smail.nju.edu.cn>
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs108
-rw-r--r--tests/ui/structs/struct-construct-with-call-issue-138931.stderr17
2 files changed, 89 insertions, 36 deletions
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 3d666055a94..97dd9faf6de 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1681,41 +1681,81 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                         // the struct literal syntax at all, as that will cause a subsequent error.
                         let fields = this.r.field_idents(def_id);
                         let has_fields = fields.as_ref().is_some_and(|f| !f.is_empty());
-                        let (fields, applicability) = match fields {
-                            Some(fields) => {
-                                let fields = if let Some(old_fields) = old_fields {
-                                    fields
-                                        .iter()
-                                        .enumerate()
-                                        .map(|(idx, new)| (new, old_fields.get(idx)))
-                                        .map(|(new, old)| {
-                                            if let Some(Some(old)) = old
-                                                && new.as_str() != old
-                                            {
-                                                format!("{new}: {old}")
-                                            } else {
-                                                new.to_string()
-                                            }
-                                        })
-                                        .collect::<Vec<String>>()
-                                } else {
-                                    fields
-                                        .iter()
-                                        .map(|f| format!("{f}{tail}"))
-                                        .collect::<Vec<String>>()
-                                };
-
-                                (fields.join(", "), applicability)
-                            }
-                            None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
-                        };
-                        let pad = if has_fields { " " } else { "" };
-                        err.span_suggestion(
+
+                        if let PathSource::Expr(Some(Expr {
+                            kind: ExprKind::Call(path, args),
                             span,
-                            format!("use struct {descr} syntax instead"),
-                            format!("{path_str} {{{pad}{fields}{pad}}}"),
-                            applicability,
-                        );
+                            ..
+                        })) = source
+                            && !args.is_empty()
+                            && let Some(fields) = &fields
+                            && args.len() == fields.len()
+                        // Make sure we have same number of args as fields
+                        {
+                            let path_span = path.span;
+                            let mut parts = Vec::new();
+
+                            // Start with the opening brace
+                            parts.push((
+                                path_span.shrink_to_hi().until(args[0].span),
+                                "{".to_owned(),
+                            ));
+
+                            for (field, arg) in fields.iter().zip(args.iter()) {
+                                // Add the field name before the argument
+                                parts.push((arg.span.shrink_to_lo(), format!("{}: ", field)));
+                            }
+
+                            // Add the closing brace
+                            parts.push((
+                                args.last().unwrap().span.shrink_to_hi().until(span.shrink_to_hi()),
+                                "}".to_owned(),
+                            ));
+
+                            err.multipart_suggestion_verbose(
+                                format!("use struct {descr} syntax instead of calling"),
+                                parts,
+                                applicability,
+                            );
+                        } else {
+                            let (fields, applicability) = match fields {
+                                Some(fields) => {
+                                    let fields = if let Some(old_fields) = old_fields {
+                                        fields
+                                            .iter()
+                                            .enumerate()
+                                            .map(|(idx, new)| (new, old_fields.get(idx)))
+                                            .map(|(new, old)| {
+                                                if let Some(Some(old)) = old
+                                                    && new.as_str() != old
+                                                {
+                                                    format!("{new}: {old}")
+                                                } else {
+                                                    new.to_string()
+                                                }
+                                            })
+                                            .collect::<Vec<String>>()
+                                    } else {
+                                        fields
+                                            .iter()
+                                            .map(|f| format!("{f}{tail}"))
+                                            .collect::<Vec<String>>()
+                                    };
+
+                                    (fields.join(", "), applicability)
+                                }
+                                None => {
+                                    ("/* fields */".to_string(), Applicability::HasPlaceholders)
+                                }
+                            };
+                            let pad = if has_fields { " " } else { "" };
+                            err.span_suggestion(
+                                span,
+                                format!("use struct {descr} syntax instead"),
+                                format!("{path_str} {{{pad}{fields}{pad}}}"),
+                                applicability,
+                            );
+                        }
                     }
                     if let PathSource::Expr(Some(Expr {
                         kind: ExprKind::Call(path, args),
diff --git a/tests/ui/structs/struct-construct-with-call-issue-138931.stderr b/tests/ui/structs/struct-construct-with-call-issue-138931.stderr
index af68b37a891..acae01df563 100644
--- a/tests/ui/structs/struct-construct-with-call-issue-138931.stderr
+++ b/tests/ui/structs/struct-construct-with-call-issue-138931.stderr
@@ -7,7 +7,13 @@ LL | | }
    | |_- `PersonOnlyName` defined here
 ...
 LL |       let wilfred = PersonOnlyName("Name1".to_owned());
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `PersonOnlyName { name: val }`
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use struct literal syntax instead of calling
+   |
+LL -     let wilfred = PersonOnlyName("Name1".to_owned());
+LL +     let wilfred = PersonOnlyName{name: "Name1".to_owned()};
+   |
 
 error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge`
   --> $DIR/struct-construct-with-call-issue-138931.rs:17:16
@@ -25,7 +31,14 @@ LL | |         "Name2".to_owned(),
 LL | |         20,
 LL | |         180,
 LL | |     );
-   | |_____^ help: use struct literal syntax instead: `PersonWithAge { name: val, age: val, height: val }`
+   | |_____^
+   |
+help: use struct literal syntax instead of calling
+   |
+LL ~     let bill = PersonWithAge{name: "Name2".to_owned(),
+LL ~         age: 20,
+LL ~         height: 180};
+   |
 
 error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge`
   --> $DIR/struct-construct-with-call-issue-138931.rs:23:18