Skip to content

Commit adf31e9

Browse files
committed
resolve: suggest variants with placeholders
This commit improves the diagnostic modified in rust-lang#77341 to suggest not only those variants which do not have fields, but those with fields (by suggesting with placeholders). Signed-off-by: David Wood <david@davidtw.co>
1 parent 7f58716 commit adf31e9

File tree

5 files changed

+311
-78
lines changed

5 files changed

+311
-78
lines changed

compiler/rustc_resolve/src/late/diagnostics.rs

+86-60
Original file line numberDiff line numberDiff line change
@@ -1330,58 +1330,32 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
13301330

13311331
let suggest_only_tuple_variants =
13321332
matches!(source, PathSource::TupleStruct(..)) || source.is_call();
1333-
let mut suggestable_variants = if suggest_only_tuple_variants {
1333+
if suggest_only_tuple_variants {
13341334
// Suggest only tuple variants regardless of whether they have fields and do not
13351335
// suggest path with added parenthesis.
1336-
variants
1336+
let mut suggestable_variants = variants
13371337
.iter()
13381338
.filter(|(.., kind)| *kind == CtorKind::Fn)
13391339
.map(|(variant, ..)| path_names_to_string(variant))
1340-
.collect::<Vec<_>>()
1341-
} else {
1342-
variants
1343-
.iter()
1344-
.filter(|(_, def_id, kind)| {
1345-
// Suggest only variants that have no fields (these can definitely
1346-
// be constructed).
1347-
let has_fields =
1348-
self.r.field_names.get(&def_id).map(|f| f.is_empty()).unwrap_or(false);
1349-
match kind {
1350-
CtorKind::Const => true,
1351-
CtorKind::Fn | CtorKind::Fictive if has_fields => true,
1352-
_ => false,
1353-
}
1354-
})
1355-
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
1356-
.map(|(variant_str, kind)| {
1357-
// Add constructor syntax where appropriate.
1358-
match kind {
1359-
CtorKind::Const => variant_str,
1360-
CtorKind::Fn => format!("({}())", variant_str),
1361-
CtorKind::Fictive => format!("({} {{}})", variant_str),
1362-
}
1363-
})
1364-
.collect::<Vec<_>>()
1365-
};
1340+
.collect::<Vec<_>>();
13661341

1367-
let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
1342+
let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
13681343

1369-
if !suggestable_variants.is_empty() {
1370-
let msg = if non_suggestable_variant_count == 0 && suggestable_variants.len() == 1 {
1371-
"try using the enum's variant"
1372-
} else {
1373-
"try using one of the enum's variants"
1374-
};
1344+
if !suggestable_variants.is_empty() {
1345+
let msg = if non_suggestable_variant_count == 0 && suggestable_variants.len() == 1 {
1346+
"try using the enum's variant"
1347+
} else {
1348+
"try using one of the enum's variants"
1349+
};
13751350

1376-
err.span_suggestions(
1377-
span,
1378-
msg,
1379-
suggestable_variants.drain(..),
1380-
Applicability::MaybeIncorrect,
1381-
);
1382-
}
1351+
err.span_suggestions(
1352+
span,
1353+
msg,
1354+
suggestable_variants.drain(..),
1355+
Applicability::MaybeIncorrect,
1356+
);
1357+
}
13831358

1384-
if suggest_only_tuple_variants {
13851359
let source_msg = if source.is_call() {
13861360
"to construct"
13871361
} else if matches!(source, PathSource::TupleStruct(..)) {
@@ -1408,24 +1382,76 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
14081382
));
14091383
}
14101384
} else {
1411-
let made_suggestion = non_suggestable_variant_count != variants.len();
1412-
if made_suggestion {
1413-
if non_suggestable_variant_count == 1 {
1414-
err.help(
1415-
"you might have meant to use the enum's other variant that has fields",
1416-
);
1417-
} else if non_suggestable_variant_count >= 1 {
1418-
err.help(
1419-
"you might have meant to use one of the enum's other variants that \
1420-
have fields",
1421-
);
1422-
}
1423-
} else {
1424-
if non_suggestable_variant_count == 1 {
1425-
err.help("you might have meant to use the enum's variant");
1426-
} else if non_suggestable_variant_count >= 1 {
1427-
err.help("you might have meant to use one of the enum's variants");
1385+
let needs_placeholder = |def_id: DefId, kind: CtorKind| {
1386+
let has_no_fields =
1387+
self.r.field_names.get(&def_id).map(|f| f.is_empty()).unwrap_or(false);
1388+
match kind {
1389+
CtorKind::Const => false,
1390+
CtorKind::Fn | CtorKind::Fictive if has_no_fields => false,
1391+
_ => true,
14281392
}
1393+
};
1394+
1395+
let mut suggestable_variants = variants
1396+
.iter()
1397+
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
1398+
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
1399+
.map(|(variant, kind)| match kind {
1400+
CtorKind::Const => variant,
1401+
CtorKind::Fn => format!("({}())", variant),
1402+
CtorKind::Fictive => format!("({} {{}})", variant),
1403+
})
1404+
.collect::<Vec<_>>();
1405+
1406+
if !suggestable_variants.is_empty() {
1407+
let msg = if suggestable_variants.len() == 1 {
1408+
"you might have meant to use the following enum variant"
1409+
} else {
1410+
"you might have meant to use one of the following enum variants"
1411+
};
1412+
1413+
err.span_suggestions(
1414+
span,
1415+
msg,
1416+
suggestable_variants.drain(..),
1417+
Applicability::MaybeIncorrect,
1418+
);
1419+
}
1420+
1421+
let mut suggestable_variants_with_placeholders = variants
1422+
.iter()
1423+
.filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind))
1424+
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
1425+
.filter_map(|(variant, kind)| match kind {
1426+
CtorKind::Fn => Some(format!("({}(/* fields */))", variant)),
1427+
CtorKind::Fictive => Some(format!("({} {{ /* fields */ }})", variant)),
1428+
_ => None,
1429+
})
1430+
.collect::<Vec<_>>();
1431+
1432+
if !suggestable_variants_with_placeholders.is_empty() {
1433+
let msg = match (
1434+
suggestable_variants.is_empty(),
1435+
suggestable_variants_with_placeholders.len(),
1436+
) {
1437+
(true, 1) => "the following enum variant is available",
1438+
(true, _) => "the following enum variants are available",
1439+
(false, 1) => "alternatively, the following enum variant is available",
1440+
(false, _) => "alternatively, the following enum variants are also available",
1441+
};
1442+
1443+
err.span_suggestions(
1444+
span,
1445+
msg,
1446+
suggestable_variants_with_placeholders.drain(..),
1447+
Applicability::HasPlaceholders,
1448+
);
1449+
}
1450+
};
1451+
1452+
if def_id.is_local() {
1453+
if let Some(span) = self.def_span(def_id) {
1454+
err.span_note(span, "the enum is defined here");
14291455
}
14301456
}
14311457
}

src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.stderr

+21
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ LL | if let Example(_) = y {
2121
| ^^^^^^^ help: try using one of the enum's variants: `Example::Ex`
2222
|
2323
= help: you might have meant to match against the enum's non-tuple variant
24+
note: the enum is defined here
25+
--> $DIR/issue-43871-enum-instead-of-variant.rs:1:1
26+
|
27+
LL | enum Example { Ex(String), NotEx }
28+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2429

2530
error[E0423]: expected function, tuple struct or tuple variant, found enum `Void`
2631
--> $DIR/issue-43871-enum-instead-of-variant.rs:31:13
@@ -29,6 +34,11 @@ LL | let y = Void();
2934
| ^^^^
3035
|
3136
= help: the enum has no tuple variants to construct
37+
note: the enum is defined here
38+
--> $DIR/issue-43871-enum-instead-of-variant.rs:3:1
39+
|
40+
LL | enum Void {}
41+
| ^^^^^^^^^^^^
3242

3343
error[E0423]: expected function, tuple struct or tuple variant, found enum `ManyVariants`
3444
--> $DIR/issue-43871-enum-instead-of-variant.rs:33:13
@@ -38,6 +48,17 @@ LL | let z = ManyVariants();
3848
|
3949
= help: the enum has no tuple variants to construct
4050
= help: you might have meant to construct one of the enum's non-tuple variants
51+
note: the enum is defined here
52+
--> $DIR/issue-43871-enum-instead-of-variant.rs:5:1
53+
|
54+
LL | / enum ManyVariants {
55+
LL | | One,
56+
LL | | Two,
57+
LL | | Three,
58+
... |
59+
LL | | Ten,
60+
LL | | }
61+
| |_^
4162

4263
error: aborting due to 5 previous errors
4364

src/test/ui/glob-resolve1.stderr

+11-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,17 @@ error[E0423]: expected value, found enum `B`
2424
--> $DIR/glob-resolve1.rs:24:5
2525
|
2626
LL | B;
27-
| ^ help: try using the enum's variant: `B::B1`
27+
| ^
28+
|
29+
note: the enum is defined here
30+
--> $DIR/glob-resolve1.rs:12:5
31+
|
32+
LL | pub enum B { B1 }
33+
| ^^^^^^^^^^^^^^^^^
34+
help: you might have meant to use the following enum variant
35+
|
36+
LL | B::B1;
37+
| ^^^^^
2838

2939
error[E0425]: cannot find value `C` in this scope
3040
--> $DIR/glob-resolve1.rs:25:5

src/test/ui/issues/issue-73427.stderr

+91-7
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,101 @@ error[E0423]: expected value, found enum `A`
44
LL | A.foo();
55
| ^
66
|
7-
= help: you might have meant to use one of the enum's other variants that have fields
8-
help: try using one of the enum's variants
7+
note: the enum is defined here
8+
--> $DIR/issue-73427.rs:1:1
9+
|
10+
LL | / enum A {
11+
LL | | StructWithFields { x: () },
12+
LL | | TupleWithFields(()),
13+
LL | | Struct {},
14+
LL | | Tuple(),
15+
LL | | Unit,
16+
LL | | }
17+
| |_^
18+
help: you might have meant to use one of the following enum variants
919
|
1020
LL | (A::Struct {}).foo();
1121
| ^^^^^^^^^^^^^^
1222
LL | (A::Tuple()).foo();
1323
| ^^^^^^^^^^^^
1424
LL | A::Unit.foo();
1525
| ^^^^^^^
26+
help: the following enum variants are available
27+
|
28+
LL | (A::StructWithFields { /* fields */ }).foo();
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
LL | (A::TupleWithFields(/* fields */)).foo();
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1632

1733
error[E0423]: expected value, found enum `B`
1834
--> $DIR/issue-73427.rs:31:5
1935
|
2036
LL | B.foo();
2137
| ^
2238
|
23-
= help: you might have meant to use one of the enum's variants
39+
note: the enum is defined here
40+
--> $DIR/issue-73427.rs:9:1
41+
|
42+
LL | / enum B {
43+
LL | | StructWithFields { x: () },
44+
LL | | TupleWithFields(()),
45+
LL | | }
46+
| |_^
47+
help: the following enum variants are available
48+
|
49+
LL | (B::StructWithFields { /* fields */ }).foo();
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
51+
LL | (B::TupleWithFields(/* fields */)).foo();
52+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2453

2554
error[E0423]: expected value, found enum `C`
2655
--> $DIR/issue-73427.rs:33:5
2756
|
2857
LL | C.foo();
29-
| ^ help: try using one of the enum's variants: `C::Unit`
58+
| ^
59+
|
60+
note: the enum is defined here
61+
--> $DIR/issue-73427.rs:14:1
3062
|
31-
= help: you might have meant to use one of the enum's other variants that have fields
63+
LL | / enum C {
64+
LL | | StructWithFields { x: () },
65+
LL | | TupleWithFields(()),
66+
LL | | Unit,
67+
LL | | }
68+
| |_^
69+
help: you might have meant to use the following enum variant
70+
|
71+
LL | C::Unit.foo();
72+
| ^^^^^^^
73+
help: the following enum variants are available
74+
|
75+
LL | (C::StructWithFields { /* fields */ }).foo();
76+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
77+
LL | (C::TupleWithFields(/* fields */)).foo();
78+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3279

3380
error[E0423]: expected value, found enum `D`
3481
--> $DIR/issue-73427.rs:35:5
3582
|
3683
LL | D.foo();
37-
| ^ help: try using one of the enum's variants: `D::Unit`
84+
| ^
85+
|
86+
note: the enum is defined here
87+
--> $DIR/issue-73427.rs:20:1
88+
|
89+
LL | / enum D {
90+
LL | | TupleWithFields(()),
91+
LL | | Unit,
92+
LL | | }
93+
| |_^
94+
help: you might have meant to use the following enum variant
3895
|
39-
= help: you might have meant to use the enum's other variant that has fields
96+
LL | D::Unit.foo();
97+
| ^^^^^^^
98+
help: the following enum variant is available
99+
|
100+
LL | (D::TupleWithFields(/* fields */)).foo();
101+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40102

41103
error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
42104
--> $DIR/issue-73427.rs:40:13
@@ -45,6 +107,17 @@ LL | let x = A(3);
45107
| ^
46108
|
47109
= help: you might have meant to construct one of the enum's non-tuple variants
110+
note: the enum is defined here
111+
--> $DIR/issue-73427.rs:1:1
112+
|
113+
LL | / enum A {
114+
LL | | StructWithFields { x: () },
115+
LL | | TupleWithFields(()),
116+
LL | | Struct {},
117+
LL | | Tuple(),
118+
LL | | Unit,
119+
LL | | }
120+
| |_^
48121
help: try using one of the enum's variants
49122
|
50123
LL | let x = A::TupleWithFields(3);
@@ -59,6 +132,17 @@ LL | if let A(3) = x { }
59132
| ^
60133
|
61134
= help: you might have meant to match against one of the enum's non-tuple variants
135+
note: the enum is defined here
136+
--> $DIR/issue-73427.rs:1:1
137+
|
138+
LL | / enum A {
139+
LL | | StructWithFields { x: () },
140+
LL | | TupleWithFields(()),
141+
LL | | Struct {},
142+
LL | | Tuple(),
143+
LL | | Unit,
144+
LL | | }
145+
| |_^
62146
help: try using one of the enum's variants
63147
|
64148
LL | if let A::TupleWithFields(3) = x { }

0 commit comments

Comments
 (0)