about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs108
-rw-r--r--tests/ui/structs/struct-construct-with-call-issue-138931.rs25
-rw-r--r--tests/ui/structs/struct-construct-with-call-issue-138931.stderr58
3 files changed, 157 insertions, 34 deletions
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index cf8db2267f4..e40f84e7e59 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1665,41 +1665,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.rs b/tests/ui/structs/struct-construct-with-call-issue-138931.rs
new file mode 100644
index 00000000000..5d50eb14bff
--- /dev/null
+++ b/tests/ui/structs/struct-construct-with-call-issue-138931.rs
@@ -0,0 +1,25 @@
+struct PersonOnlyName {
+    name: String
+}
+
+struct PersonWithAge {
+    name: String,
+    age: u8,
+    height: u8,
+}
+
+
+
+fn main() {
+    let wilfred = PersonOnlyName("Name1".to_owned());
+    //~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonOnlyName` [E0423]
+
+    let bill = PersonWithAge( //~ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423]
+        "Name2".to_owned(),
+        20,
+        180,
+    );
+
+    let person = PersonWithAge("Name3".to_owned());
+    //~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423]
+}
diff --git a/tests/ui/structs/struct-construct-with-call-issue-138931.stderr b/tests/ui/structs/struct-construct-with-call-issue-138931.stderr
new file mode 100644
index 00000000000..acae01df563
--- /dev/null
+++ b/tests/ui/structs/struct-construct-with-call-issue-138931.stderr
@@ -0,0 +1,58 @@
+error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonOnlyName`
+  --> $DIR/struct-construct-with-call-issue-138931.rs:14:19
+   |
+LL | / struct PersonOnlyName {
+LL | |     name: String
+LL | | }
+   | |_- `PersonOnlyName` defined here
+...
+LL |       let wilfred = PersonOnlyName("Name1".to_owned());
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+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
+   |
+LL | / struct PersonWithAge {
+LL | |     name: String,
+LL | |     age: u8,
+LL | |     height: u8,
+LL | | }
+   | |_- `PersonWithAge` defined here
+...
+LL |       let bill = PersonWithAge(
+   |  ________________^
+LL | |         "Name2".to_owned(),
+LL | |         20,
+LL | |         180,
+LL | |     );
+   | |_____^
+   |
+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
+   |
+LL | / struct PersonWithAge {
+LL | |     name: String,
+LL | |     age: u8,
+LL | |     height: u8,
+LL | | }
+   | |_- `PersonWithAge` defined here
+...
+LL |       let person = PersonWithAge("Name3".to_owned());
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `PersonWithAge { name: val, age: val, height: val }`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0423`.