Skip to content

Commit 9342661

Browse files
committed
Auto merge of #45942 - Menschenkindlein:master, r=estebank
Add hints for the case of confusing enum with its variants A solution for #43871. When one uses an enum in a place that accepts variants (e.g., `Option(result)` instead of `Some(result)`), suggest one of this enum's variants. cc @estebank
2 parents bbd7932 + a3686c6 commit 9342661

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

src/librustc_resolve/lib.rs

+82
Original file line numberDiff line numberDiff line change
@@ -2614,6 +2614,22 @@ impl<'a> Resolver<'a> {
26142614
}
26152615
_ => {}
26162616
},
2617+
(Def::Enum(..), PathSource::TupleStruct)
2618+
| (Def::Enum(..), PathSource::Expr(..)) => {
2619+
if let Some(variants) = this.collect_enum_variants(def) {
2620+
err.note(&format!("did you mean to use one \
2621+
of the following variants?\n{}",
2622+
variants.iter()
2623+
.map(|suggestion| path_names_to_string(suggestion))
2624+
.map(|suggestion| format!("- `{}`", suggestion))
2625+
.collect::<Vec<_>>()
2626+
.join("\n")));
2627+
2628+
} else {
2629+
err.note("did you mean to use one of the enum's variants?");
2630+
}
2631+
return (err, candidates);
2632+
},
26172633
_ if ns == ValueNS && is_struct_like(def) => {
26182634
if let Def::Struct(def_id) = def {
26192635
if let Some((ctor_def, ctor_vis))
@@ -3540,6 +3556,72 @@ impl<'a> Resolver<'a> {
35403556
candidates
35413557
}
35423558

3559+
fn find_module(&mut self,
3560+
module_def: Def)
3561+
-> Option<(Module<'a>, ImportSuggestion)>
3562+
{
3563+
let mut result = None;
3564+
let mut worklist = Vec::new();
3565+
let mut seen_modules = FxHashSet();
3566+
worklist.push((self.graph_root, Vec::new()));
3567+
3568+
while let Some((in_module, path_segments)) = worklist.pop() {
3569+
// abort if the module is already found
3570+
if let Some(_) = result { break; }
3571+
3572+
self.populate_module_if_necessary(in_module);
3573+
3574+
in_module.for_each_child_stable(|ident, _, name_binding| {
3575+
// abort if the module is already found
3576+
if let Some(_) = result {
3577+
return ();
3578+
}
3579+
if let Some(module) = name_binding.module() {
3580+
// form the path
3581+
let mut path_segments = path_segments.clone();
3582+
path_segments.push(ast::PathSegment::from_ident(ident, name_binding.span));
3583+
if module.def() == Some(module_def) {
3584+
let path = Path {
3585+
span: name_binding.span,
3586+
segments: path_segments,
3587+
};
3588+
result = Some((module, ImportSuggestion { path: path }));
3589+
} else {
3590+
// add the module to the lookup
3591+
if seen_modules.insert(module.def_id().unwrap()) {
3592+
worklist.push((module, path_segments));
3593+
}
3594+
}
3595+
}
3596+
});
3597+
}
3598+
3599+
result
3600+
}
3601+
3602+
fn collect_enum_variants(&mut self, enum_def: Def) -> Option<Vec<Path>> {
3603+
if let Def::Enum(..) = enum_def {} else {
3604+
panic!("Non-enum def passed to collect_enum_variants: {:?}", enum_def)
3605+
}
3606+
3607+
self.find_module(enum_def).map(|(enum_module, enum_import_suggestion)| {
3608+
self.populate_module_if_necessary(enum_module);
3609+
3610+
let mut variants = Vec::new();
3611+
enum_module.for_each_child_stable(|ident, _, name_binding| {
3612+
if let Def::Variant(..) = name_binding.def() {
3613+
let mut segms = enum_import_suggestion.path.segments.clone();
3614+
segms.push(ast::PathSegment::from_ident(ident, name_binding.span));
3615+
variants.push(Path {
3616+
span: name_binding.span,
3617+
segments: segms,
3618+
});
3619+
}
3620+
});
3621+
variants
3622+
})
3623+
}
3624+
35433625
fn record_def(&mut self, node_id: NodeId, resolution: PathResolution) {
35443626
debug!("(recording def) recording {:?} for {}", resolution, node_id);
35453627
if let Some(prev_res) = self.def_map.insert(node_id, resolution) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum Example { Ex(String), NotEx }
12+
13+
fn result_test() {
14+
let x = Option(1);
15+
16+
if let Option(_) = x {
17+
println!("It is OK.");
18+
}
19+
20+
let y = Example::Ex(String::from("test"));
21+
22+
if let Example(_) = y {
23+
println!("It is OK.");
24+
}
25+
}
26+
27+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
error[E0423]: expected function, found enum `Option`
2+
--> $DIR/issue-43871-enum-instead-of-variant.rs:14:13
3+
|
4+
14 | let x = Option(1);
5+
| ^^^^^^
6+
|
7+
= note: did you mean to use one of the following variants?
8+
- `std::prelude::v1::Option::None`
9+
- `std::prelude::v1::Option::Some`
10+
11+
error[E0532]: expected tuple struct/variant, found enum `Option`
12+
--> $DIR/issue-43871-enum-instead-of-variant.rs:16:12
13+
|
14+
16 | if let Option(_) = x {
15+
| ^^^^^^
16+
|
17+
= note: did you mean to use one of the following variants?
18+
- `std::prelude::v1::Option::None`
19+
- `std::prelude::v1::Option::Some`
20+
21+
error[E0532]: expected tuple struct/variant, found enum `Example`
22+
--> $DIR/issue-43871-enum-instead-of-variant.rs:22:12
23+
|
24+
22 | if let Example(_) = y {
25+
| ^^^^^^^
26+
|
27+
= note: did you mean to use one of the following variants?
28+
- `Example::Ex`
29+
- `Example::NotEx`
30+
31+
error: aborting due to 3 previous errors
32+

0 commit comments

Comments
 (0)