Skip to content

Commit 75ef735

Browse files
authored
Rollup merge of rust-lang#77855 - davidtwco:pr-77341-follow-up-non-constructable-variants, r=estebank
resolve: further improvements to "try using the enum's variant" diagnostic Follow-up on rust-lang#77341 (comment). This PR 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). In addition, the wording of the tuple-variant-only case is improved slightly. I've not made further changes to the tuple-variant-only case (e.g. to only suggest variants with the correct number of fields) because I don't think I have enough information to do so reliably (e.g. in the case where there is an attempt to construct a tuple variant, I have no information on how many fields were provided; and in the case of pattern matching, I only have a slice of spans and would need to check for things like `..` in those spans, which doesn't seem worth it). r? @estebank
2 parents 8c4d855 + f897162 commit 75ef735

File tree

5 files changed

+318
-85
lines changed

5 files changed

+318
-85
lines changed

compiler/rustc_resolve/src/late/diagnostics.rs

+88-62
Original file line numberDiff line numberDiff line change
@@ -1330,58 +1330,17 @@ 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-
};
1366-
1367-
let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
1340+
.collect::<Vec<_>>();
13681341

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-
};
1342+
let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
13751343

1376-
err.span_suggestions(
1377-
span,
1378-
msg,
1379-
suggestable_variants.drain(..),
1380-
Applicability::MaybeIncorrect,
1381-
);
1382-
}
1383-
1384-
if suggest_only_tuple_variants {
13851344
let source_msg = if source.is_call() {
13861345
"to construct"
13871346
} else if matches!(source, PathSource::TupleStruct(..)) {
@@ -1390,6 +1349,21 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
13901349
unreachable!()
13911350
};
13921351

1352+
if !suggestable_variants.is_empty() {
1353+
let msg = if non_suggestable_variant_count == 0 && suggestable_variants.len() == 1 {
1354+
format!("try {} the enum's variant", source_msg)
1355+
} else {
1356+
format!("try {} one of the enum's variants", source_msg)
1357+
};
1358+
1359+
err.span_suggestions(
1360+
span,
1361+
&msg,
1362+
suggestable_variants.drain(..),
1363+
Applicability::MaybeIncorrect,
1364+
);
1365+
}
1366+
13931367
// If the enum has no tuple variants..
13941368
if non_suggestable_variant_count == variants.len() {
13951369
err.help(&format!("the enum has no tuple variants {}", source_msg));
@@ -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

+24-3
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,30 @@ error[E0423]: expected function, tuple struct or tuple variant, found enum `Opti
22
--> $DIR/issue-43871-enum-instead-of-variant.rs:19:13
33
|
44
LL | let x = Option(1);
5-
| ^^^^^^ help: try using one of the enum's variants: `std::option::Option::Some`
5+
| ^^^^^^ help: try to construct one of the enum's variants: `std::option::Option::Some`
66
|
77
= help: you might have meant to construct the enum's non-tuple variant
88

99
error[E0532]: expected tuple struct or tuple variant, found enum `Option`
1010
--> $DIR/issue-43871-enum-instead-of-variant.rs:21:12
1111
|
1212
LL | if let Option(_) = x {
13-
| ^^^^^^ help: try using one of the enum's variants: `std::option::Option::Some`
13+
| ^^^^^^ help: try to match against one of the enum's variants: `std::option::Option::Some`
1414
|
1515
= help: you might have meant to match against the enum's non-tuple variant
1616

1717
error[E0532]: expected tuple struct or tuple variant, found enum `Example`
1818
--> $DIR/issue-43871-enum-instead-of-variant.rs:27:12
1919
|
2020
LL | if let Example(_) = y {
21-
| ^^^^^^^ help: try using one of the enum's variants: `Example::Ex`
21+
| ^^^^^^^ help: try to match against 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

0 commit comments

Comments
 (0)