Auto merge of #45942 - Menschenkindlein:master, r=estebank
Add hints for the case of confusing enum with its variants A solution for https://github.com/rust-lang/rust/issues/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
This commit is contained in:
commit
93426613a7
@ -2614,6 +2614,22 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
(Def::Enum(..), PathSource::TupleStruct)
|
||||
| (Def::Enum(..), PathSource::Expr(..)) => {
|
||||
if let Some(variants) = this.collect_enum_variants(def) {
|
||||
err.note(&format!("did you mean to use one \
|
||||
of the following variants?\n{}",
|
||||
variants.iter()
|
||||
.map(|suggestion| path_names_to_string(suggestion))
|
||||
.map(|suggestion| format!("- `{}`", suggestion))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")));
|
||||
|
||||
} else {
|
||||
err.note("did you mean to use one of the enum's variants?");
|
||||
}
|
||||
return (err, candidates);
|
||||
},
|
||||
_ if ns == ValueNS && is_struct_like(def) => {
|
||||
if let Def::Struct(def_id) = def {
|
||||
if let Some((ctor_def, ctor_vis))
|
||||
@ -3540,6 +3556,72 @@ impl<'a> Resolver<'a> {
|
||||
candidates
|
||||
}
|
||||
|
||||
fn find_module(&mut self,
|
||||
module_def: Def)
|
||||
-> Option<(Module<'a>, ImportSuggestion)>
|
||||
{
|
||||
let mut result = None;
|
||||
let mut worklist = Vec::new();
|
||||
let mut seen_modules = FxHashSet();
|
||||
worklist.push((self.graph_root, Vec::new()));
|
||||
|
||||
while let Some((in_module, path_segments)) = worklist.pop() {
|
||||
// abort if the module is already found
|
||||
if let Some(_) = result { break; }
|
||||
|
||||
self.populate_module_if_necessary(in_module);
|
||||
|
||||
in_module.for_each_child_stable(|ident, _, name_binding| {
|
||||
// abort if the module is already found
|
||||
if let Some(_) = result {
|
||||
return ();
|
||||
}
|
||||
if let Some(module) = name_binding.module() {
|
||||
// form the path
|
||||
let mut path_segments = path_segments.clone();
|
||||
path_segments.push(ast::PathSegment::from_ident(ident, name_binding.span));
|
||||
if module.def() == Some(module_def) {
|
||||
let path = Path {
|
||||
span: name_binding.span,
|
||||
segments: path_segments,
|
||||
};
|
||||
result = Some((module, ImportSuggestion { path: path }));
|
||||
} else {
|
||||
// add the module to the lookup
|
||||
if seen_modules.insert(module.def_id().unwrap()) {
|
||||
worklist.push((module, path_segments));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn collect_enum_variants(&mut self, enum_def: Def) -> Option<Vec<Path>> {
|
||||
if let Def::Enum(..) = enum_def {} else {
|
||||
panic!("Non-enum def passed to collect_enum_variants: {:?}", enum_def)
|
||||
}
|
||||
|
||||
self.find_module(enum_def).map(|(enum_module, enum_import_suggestion)| {
|
||||
self.populate_module_if_necessary(enum_module);
|
||||
|
||||
let mut variants = Vec::new();
|
||||
enum_module.for_each_child_stable(|ident, _, name_binding| {
|
||||
if let Def::Variant(..) = name_binding.def() {
|
||||
let mut segms = enum_import_suggestion.path.segments.clone();
|
||||
segms.push(ast::PathSegment::from_ident(ident, name_binding.span));
|
||||
variants.push(Path {
|
||||
span: name_binding.span,
|
||||
segments: segms,
|
||||
});
|
||||
}
|
||||
});
|
||||
variants
|
||||
})
|
||||
}
|
||||
|
||||
fn record_def(&mut self, node_id: NodeId, resolution: PathResolution) {
|
||||
debug!("(recording def) recording {:?} for {}", resolution, node_id);
|
||||
if let Some(prev_res) = self.def_map.insert(node_id, resolution) {
|
||||
|
@ -0,0 +1,27 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
enum Example { Ex(String), NotEx }
|
||||
|
||||
fn result_test() {
|
||||
let x = Option(1);
|
||||
|
||||
if let Option(_) = x {
|
||||
println!("It is OK.");
|
||||
}
|
||||
|
||||
let y = Example::Ex(String::from("test"));
|
||||
|
||||
if let Example(_) = y {
|
||||
println!("It is OK.");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,32 @@
|
||||
error[E0423]: expected function, found enum `Option`
|
||||
--> $DIR/issue-43871-enum-instead-of-variant.rs:14:13
|
||||
|
|
||||
14 | let x = Option(1);
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: did you mean to use one of the following variants?
|
||||
- `std::prelude::v1::Option::None`
|
||||
- `std::prelude::v1::Option::Some`
|
||||
|
||||
error[E0532]: expected tuple struct/variant, found enum `Option`
|
||||
--> $DIR/issue-43871-enum-instead-of-variant.rs:16:12
|
||||
|
|
||||
16 | if let Option(_) = x {
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: did you mean to use one of the following variants?
|
||||
- `std::prelude::v1::Option::None`
|
||||
- `std::prelude::v1::Option::Some`
|
||||
|
||||
error[E0532]: expected tuple struct/variant, found enum `Example`
|
||||
--> $DIR/issue-43871-enum-instead-of-variant.rs:22:12
|
||||
|
|
||||
22 | if let Example(_) = y {
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: did you mean to use one of the following variants?
|
||||
- `Example::Ex`
|
||||
- `Example::NotEx`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user