Skip to content

Commit d517a4f

Browse files
authored
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
2 parents ed75219 + 4648650 commit d517a4f

File tree

3 files changed

+157
-34
lines changed

3 files changed

+157
-34
lines changed

compiler/rustc_resolve/src/late/diagnostics.rs

+74-34
Original file line numberDiff line numberDiff line change
@@ -1665,41 +1665,81 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
16651665
// the struct literal syntax at all, as that will cause a subsequent error.
16661666
let fields = this.r.field_idents(def_id);
16671667
let has_fields = fields.as_ref().is_some_and(|f| !f.is_empty());
1668-
let (fields, applicability) = match fields {
1669-
Some(fields) => {
1670-
let fields = if let Some(old_fields) = old_fields {
1671-
fields
1672-
.iter()
1673-
.enumerate()
1674-
.map(|(idx, new)| (new, old_fields.get(idx)))
1675-
.map(|(new, old)| {
1676-
if let Some(Some(old)) = old
1677-
&& new.as_str() != old
1678-
{
1679-
format!("{new}: {old}")
1680-
} else {
1681-
new.to_string()
1682-
}
1683-
})
1684-
.collect::<Vec<String>>()
1685-
} else {
1686-
fields
1687-
.iter()
1688-
.map(|f| format!("{f}{tail}"))
1689-
.collect::<Vec<String>>()
1690-
};
1691-
1692-
(fields.join(", "), applicability)
1693-
}
1694-
None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
1695-
};
1696-
let pad = if has_fields { " " } else { "" };
1697-
err.span_suggestion(
1668+
1669+
if let PathSource::Expr(Some(Expr {
1670+
kind: ExprKind::Call(path, args),
16981671
span,
1699-
format!("use struct {descr} syntax instead"),
1700-
format!("{path_str} {{{pad}{fields}{pad}}}"),
1701-
applicability,
1702-
);
1672+
..
1673+
})) = source
1674+
&& !args.is_empty()
1675+
&& let Some(fields) = &fields
1676+
&& args.len() == fields.len()
1677+
// Make sure we have same number of args as fields
1678+
{
1679+
let path_span = path.span;
1680+
let mut parts = Vec::new();
1681+
1682+
// Start with the opening brace
1683+
parts.push((
1684+
path_span.shrink_to_hi().until(args[0].span),
1685+
"{".to_owned(),
1686+
));
1687+
1688+
for (field, arg) in fields.iter().zip(args.iter()) {
1689+
// Add the field name before the argument
1690+
parts.push((arg.span.shrink_to_lo(), format!("{}: ", field)));
1691+
}
1692+
1693+
// Add the closing brace
1694+
parts.push((
1695+
args.last().unwrap().span.shrink_to_hi().until(span.shrink_to_hi()),
1696+
"}".to_owned(),
1697+
));
1698+
1699+
err.multipart_suggestion_verbose(
1700+
format!("use struct {descr} syntax instead of calling"),
1701+
parts,
1702+
applicability,
1703+
);
1704+
} else {
1705+
let (fields, applicability) = match fields {
1706+
Some(fields) => {
1707+
let fields = if let Some(old_fields) = old_fields {
1708+
fields
1709+
.iter()
1710+
.enumerate()
1711+
.map(|(idx, new)| (new, old_fields.get(idx)))
1712+
.map(|(new, old)| {
1713+
if let Some(Some(old)) = old
1714+
&& new.as_str() != old
1715+
{
1716+
format!("{new}: {old}")
1717+
} else {
1718+
new.to_string()
1719+
}
1720+
})
1721+
.collect::<Vec<String>>()
1722+
} else {
1723+
fields
1724+
.iter()
1725+
.map(|f| format!("{f}{tail}"))
1726+
.collect::<Vec<String>>()
1727+
};
1728+
1729+
(fields.join(", "), applicability)
1730+
}
1731+
None => {
1732+
("/* fields */".to_string(), Applicability::HasPlaceholders)
1733+
}
1734+
};
1735+
let pad = if has_fields { " " } else { "" };
1736+
err.span_suggestion(
1737+
span,
1738+
format!("use struct {descr} syntax instead"),
1739+
format!("{path_str} {{{pad}{fields}{pad}}}"),
1740+
applicability,
1741+
);
1742+
}
17031743
}
17041744
if let PathSource::Expr(Some(Expr {
17051745
kind: ExprKind::Call(path, args),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
struct PersonOnlyName {
2+
name: String
3+
}
4+
5+
struct PersonWithAge {
6+
name: String,
7+
age: u8,
8+
height: u8,
9+
}
10+
11+
12+
13+
fn main() {
14+
let wilfred = PersonOnlyName("Name1".to_owned());
15+
//~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonOnlyName` [E0423]
16+
17+
let bill = PersonWithAge( //~ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423]
18+
"Name2".to_owned(),
19+
20,
20+
180,
21+
);
22+
23+
let person = PersonWithAge("Name3".to_owned());
24+
//~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423]
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonOnlyName`
2+
--> $DIR/struct-construct-with-call-issue-138931.rs:14:19
3+
|
4+
LL | / struct PersonOnlyName {
5+
LL | | name: String
6+
LL | | }
7+
| |_- `PersonOnlyName` defined here
8+
...
9+
LL | let wilfred = PersonOnlyName("Name1".to_owned());
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11+
|
12+
help: use struct literal syntax instead of calling
13+
|
14+
LL - let wilfred = PersonOnlyName("Name1".to_owned());
15+
LL + let wilfred = PersonOnlyName{name: "Name1".to_owned()};
16+
|
17+
18+
error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge`
19+
--> $DIR/struct-construct-with-call-issue-138931.rs:17:16
20+
|
21+
LL | / struct PersonWithAge {
22+
LL | | name: String,
23+
LL | | age: u8,
24+
LL | | height: u8,
25+
LL | | }
26+
| |_- `PersonWithAge` defined here
27+
...
28+
LL | let bill = PersonWithAge(
29+
| ________________^
30+
LL | | "Name2".to_owned(),
31+
LL | | 20,
32+
LL | | 180,
33+
LL | | );
34+
| |_____^
35+
|
36+
help: use struct literal syntax instead of calling
37+
|
38+
LL ~ let bill = PersonWithAge{name: "Name2".to_owned(),
39+
LL ~ age: 20,
40+
LL ~ height: 180};
41+
|
42+
43+
error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge`
44+
--> $DIR/struct-construct-with-call-issue-138931.rs:23:18
45+
|
46+
LL | / struct PersonWithAge {
47+
LL | | name: String,
48+
LL | | age: u8,
49+
LL | | height: u8,
50+
LL | | }
51+
| |_- `PersonWithAge` defined here
52+
...
53+
LL | let person = PersonWithAge("Name3".to_owned());
54+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `PersonWithAge { name: val, age: val, height: val }`
55+
56+
error: aborting due to 3 previous errors
57+
58+
For more information about this error, try `rustc --explain E0423`.

0 commit comments

Comments
 (0)