From 4648650d899ba01a6ed30e7ada3795b5c56465eb Mon Sep 17 00:00:00 2001 From: xizheyin Date: Thu, 27 Mar 2025 17:28:15 +0800 Subject: [PATCH] Improve suggest construct with literal syntax instead of calling Signed-off-by: xizheyin --- .../rustc_resolve/src/late/diagnostics.rs | 106 ++++++++++++------ ...ct-construct-with-call-issue-138931.stderr | 17 ++- 2 files changed, 88 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 3d666055a94f..97dd9faf6dec 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1681,41 +1681,81 @@ fn smart_resolve_context_dependent_help( // 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::>() - } else { - fields - .iter() - .map(|f| format!("{f}{tail}")) - .collect::>() - }; - (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::>() + } else { + fields + .iter() + .map(|f| format!("{f}{tail}")) + .collect::>() + }; + + (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 af68b37a891e..acae01df5636 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