about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMaxim Zholobak <ghostcheka@gmail.com>2017-09-30 11:27:02 +0300
committerMaxim Zholobak <ghostcheka@gmail.com>2017-11-13 09:37:55 +0200
commit21c580e4c4c9e6f2772fddf8d5ad7824696f7fc8 (patch)
treee4f8f5c8ef57a82aa0b86cc6d10aad31d5b98b5f
parentc703ff275fd84bd44b6618a8a59e102fee94e635 (diff)
downloadrust-21c580e4c4c9e6f2772fddf8d5ad7824696f7fc8.tar.gz
rust-21c580e4c4c9e6f2772fddf8d5ad7824696f7fc8.zip
Add hints for the case of confusing enum with its variants
-rw-r--r--src/librustc_resolve/lib.rs79
1 files changed, 79 insertions, 0 deletions
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 8207057fd0a..502dcd1b7e8 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2599,6 +2599,21 @@ impl<'a> Resolver<'a> {
                         }
                         _ => {}
                     },
+                    (Def::Enum(..), PathSource::TupleStruct) => {
+                        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| format!("- `{}`",
+                                                              path_names_to_string(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))
@@ -3495,6 +3510,70 @@ 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(|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)| {
+            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) {