diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 2bd08d2c798..3243152527f 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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::>() + .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> { + 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) { diff --git a/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.rs b/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.rs new file mode 100644 index 00000000000..923b0984d06 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.stderr b/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.stderr new file mode 100644 index 00000000000..8371413c5a2 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.stderr @@ -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 +