diff options
| author | xizheyin <xizheyin@smail.nju.edu.cn> | 2025-03-27 17:28:15 +0800 |
|---|---|---|
| committer | xizheyin <xizheyin@smail.nju.edu.cn> | 2025-03-27 20:09:37 +0800 |
| commit | 4648650d899ba01a6ed30e7ada3795b5c56465eb (patch) | |
| tree | d79ac25536661d513b99c5df3e44ad640c3bcca7 | |
| parent | d0353f5c7ac0c73c6ae97623ac3e01fab1f657ae (diff) | |
| download | rust-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.rs | 108 | ||||
| -rw-r--r-- | tests/ui/structs/struct-construct-with-call-issue-138931.stderr | 17 |
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 |
