Rollup merge of #139014 - xizheyin:issue-138931, r=oli-obk
Improve suggest construct with literal syntax instead of calling Closing #138931 When constructing a structure through a format similar to calling a constructor, we can use verbose suggestions to hint at using literal syntax for clearer advice. The case of multiple fields is also considered here, provided that the field has the same number of arguments as CallExpr. r? compiler
This commit is contained in:
commit
d517a4f0ae
3 changed files with 156 additions and 33 deletions
|
@ -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),
|
||||
|
|
25
tests/ui/structs/struct-construct-with-call-issue-138931.rs
Normal file
25
tests/ui/structs/struct-construct-with-call-issue-138931.rs
Normal file
|
@ -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]
|
||||
}
|
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue