]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_resolve/resolve_imports.rs
#[feature(uniform_paths)]: allow `use x::y;` to resolve through `self::x`, not just...
[rust.git] / src / librustc_resolve / resolve_imports.rs
index a3a9b938bbd6fd859b962bf12b2c942f321d1519..7209291aebdb5ce86bf588f5e3a3d7668ca7e472 100644 (file)
@@ -10,7 +10,7 @@
 
 use self::ImportDirectiveSubclass::*;
 
-use {AmbiguityError, CrateLint, Module, PerNS};
+use {AmbiguityError, CrateLint, Module, ModuleOrUniformRoot, PerNS};
 use Namespace::{self, TypeNS, MacroNS};
 use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
 use Resolver;
@@ -85,7 +85,8 @@ pub struct ImportDirective<'a> {
 
     pub parent: Module<'a>,
     pub module_path: Vec<Ident>,
-    pub imported_module: Cell<Option<Module<'a>>>, // the resolution of `module_path`
+    /// The resolution of `module_path`.
+    pub imported_module: Cell<Option<ModuleOrUniformRoot<'a>>>,
     pub subclass: ImportDirectiveSubclass<'a>,
     pub vis: Cell<ty::Visibility>,
     pub expansion: Mark,
@@ -133,19 +134,96 @@ fn resolution(&self, module: Module<'a>, ident: Ident, ns: Namespace)
     /// Attempts to resolve `ident` in namespaces `ns` of `module`.
     /// Invariant: if `record_used` is `Some`, expansion and import resolution must be complete.
     pub fn resolve_ident_in_module_unadjusted(&mut self,
-                                              module: Module<'a>,
+                                              module: ModuleOrUniformRoot<'a>,
                                               ident: Ident,
                                               ns: Namespace,
                                               restricted_shadowing: bool,
                                               record_used: bool,
                                               path_span: Span)
                                               -> Result<&'a NameBinding<'a>, Determinacy> {
+        let module = match module {
+            ModuleOrUniformRoot::Module(module) => module,
+            ModuleOrUniformRoot::UniformRoot(root) => {
+                // HACK(eddyb): `resolve_path` uses `keywords::Invalid` to indicate
+                // paths of length 0, and currently these are relative `use` paths.
+                let can_be_relative = !ident.is_path_segment_keyword() &&
+                    root == keywords::Invalid.name();
+                if can_be_relative {
+                    // Relative paths should only get here if the feature-gate is on.
+                    assert!(self.session.rust_2018() &&
+                            self.session.features_untracked().uniform_paths);
+
+                    // Try first to resolve relatively.
+                    let mut ctxt = ident.span.ctxt().modern();
+                    let self_module = self.resolve_self(&mut ctxt, self.current_module);
+
+                    let binding = self.resolve_ident_in_module_unadjusted(
+                        ModuleOrUniformRoot::Module(self_module),
+                        ident,
+                        ns,
+                        restricted_shadowing,
+                        record_used,
+                        path_span,
+                    );
+
+                    // FIXME(eddyb) This may give false negatives, specifically
+                    // if a crate with the same name is found in `extern_prelude`,
+                    // preventing the check below this one from returning `binding`
+                    // in all cases.
+                    //
+                    // That is, if there's no crate with the same name, `binding`
+                    // is always returned, which is the result of doing the exact
+                    // same lookup of `ident`, in the `self` module.
+                    // But when a crate does exist, it will get chosen even when
+                    // macro expansion could result in a success from the lookup
+                    // in the `self` module, later on.
+                    if binding.is_ok() {
+                        return binding;
+                    }
+
+                    // Fall back to resolving to an external crate.
+                    if !self.extern_prelude.contains(&ident.name) {
+                        // ... unless the crate name is not in the `extern_prelude`.
+                        return binding;
+                    }
+                }
+
+                let crate_root = if
+                    root != keywords::Extern.name() &&
+                    (
+                        ident.name == keywords::Crate.name() ||
+                        ident.name == keywords::DollarCrate.name()
+                    )
+                {
+                    self.resolve_crate_root(ident)
+                } else if !ident.is_path_segment_keyword() {
+                    let crate_id =
+                        self.crate_loader.process_path_extern(ident.name, ident.span);
+                    self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX })
+                } else {
+                    return Err(Determined);
+                };
+                self.populate_module_if_necessary(crate_root);
+                let binding = (crate_root, ty::Visibility::Public,
+                               ident.span, Mark::root()).to_name_binding(self.arenas);
+                return Ok(binding);
+            }
+        };
+
         self.populate_module_if_necessary(module);
 
         let resolution = self.resolution(module, ident, ns)
             .try_borrow_mut()
             .map_err(|_| Determined)?; // This happens when there is a cycle of imports
 
+        if let Some(binding) = resolution.binding {
+            if !restricted_shadowing && binding.expansion != Mark::root() {
+                if let NameBindingKind::Def(_, true) = binding.kind {
+                    self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
+                }
+            }
+        }
+
         if record_used {
             if let Some(binding) = resolution.binding {
                 if let Some(shadowed_glob) = resolution.shadowed_glob {
@@ -211,9 +289,15 @@ pub fn resolve_ident_in_module_unadjusted(&mut self,
         // if it cannot be shadowed by some new item/import expanded from a macro.
         // This happens either if there are no unexpanded macros, or expanded names cannot
         // shadow globs (that happens in macro namespace or with restricted shadowing).
-        let unexpanded_macros = !module.unresolved_invocations.borrow().is_empty() ||
-                                (ns == MacroNS && ptr::eq(module, self.graph_root) &&
-                                 !self.unresolved_invocations_macro_export.is_empty());
+        //
+        // Additionally, any macro in any module can plant names in the root module if it creates
+        // `macro_export` macros, so the root module effectively has unresolved invocations if any
+        // module has unresolved invocations.
+        // However, it causes resolution/expansion to stuck too often (#53144), so, to make
+        // progress, we have to ignore those potential unresolved invocations from other modules
+        // and prohibit access to macro-expanded `macro_export` macros instead (unless restricted
+        // shadowing is enabled, see `macro_expanded_macro_export_errors`).
+        let unexpanded_macros = !module.unresolved_invocations.borrow().is_empty();
         if let Some(binding) = resolution.binding {
             if !unexpanded_macros || ns == MacroNS || restricted_shadowing {
                 return check_usable(self, binding);
@@ -246,7 +330,11 @@ pub fn resolve_ident_in_module_unadjusted(&mut self,
             if !self.is_accessible(glob_import.vis.get()) {
                 continue
             }
-            let module = unwrap_or!(glob_import.imported_module.get(), return Err(Undetermined));
+            let module = match glob_import.imported_module.get() {
+                Some(ModuleOrUniformRoot::Module(module)) => module,
+                Some(ModuleOrUniformRoot::UniformRoot(_)) => continue,
+                None => return Err(Undetermined),
+            };
             let (orig_current_module, mut ident) = (self.current_module, ident.modern());
             match ident.span.glob_adjust(module.expansion, glob_import.span.ctxt().modern()) {
                 Some(Some(def)) => self.current_module = self.macro_def_scope(def),
@@ -254,7 +342,12 @@ pub fn resolve_ident_in_module_unadjusted(&mut self,
                 None => continue,
             };
             let result = self.resolve_ident_in_module_unadjusted(
-                module, ident, ns, false, false, path_span,
+                ModuleOrUniformRoot::Module(module),
+                ident,
+                ns,
+                false,
+                false,
+                path_span,
             );
             self.current_module = orig_current_module;
             match result {
@@ -562,8 +655,14 @@ fn resolve_import(&mut self, directive: &'b ImportDirective<'b>) -> bool {
             // For better failure detection, pretend that the import will not define any names
             // while resolving its module path.
             directive.vis.set(ty::Visibility::Invisible);
-            let result = self.resolve_path(&directive.module_path[..], None, false,
-                                           directive.span, directive.crate_lint());
+            let result = self.resolve_path(
+                Some(ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name())),
+                &directive.module_path[..],
+                None,
+                false,
+                directive.span,
+                directive.crate_lint(),
+            );
             directive.vis.set(vis);
 
             match result {
@@ -630,77 +729,9 @@ fn resolve_import(&mut self, directive: &'b ImportDirective<'b>) -> bool {
     fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Span, String)> {
         self.current_module = directive.parent;
         let ImportDirective { ref module_path, span, .. } = *directive;
-        let mut warn_if_binding_comes_from_local_crate = false;
-
-        // FIXME: Last path segment is treated specially in import resolution, so extern crate
-        // mode for absolute paths needs some special support for single-segment imports.
-        if module_path.len() == 1 && (module_path[0].name == keywords::CrateRoot.name() ||
-                                      module_path[0].name == keywords::Extern.name()) {
-            let is_extern = module_path[0].name == keywords::Extern.name() ||
-                            (self.session.features_untracked().extern_absolute_paths &&
-                             self.session.rust_2018());
-            match directive.subclass {
-                GlobImport { .. } if is_extern => {
-                    return Some((directive.span,
-                                 "cannot glob-import all possible crates".to_string()));
-                }
-                GlobImport { .. } if self.session.features_untracked().extern_absolute_paths => {
-                    self.lint_path_starts_with_module(
-                        directive.root_id,
-                        directive.root_span,
-                    );
-                }
-                SingleImport { source, target, .. } => {
-                    let crate_root = if source.name == keywords::Crate.name() &&
-                                        module_path[0].name != keywords::Extern.name() {
-                        if target.name == keywords::Crate.name() {
-                            return Some((directive.span,
-                                         "crate root imports need to be explicitly named: \
-                                          `use crate as name;`".to_string()));
-                        } else {
-                            Some(self.resolve_crate_root(source))
-                        }
-                    } else if is_extern && !source.is_path_segment_keyword() {
-                        let crate_id =
-                            self.resolver.crate_loader.process_use_extern(
-                                source.name,
-                                directive.span,
-                                directive.id,
-                                &self.resolver.definitions,
-                            );
-                        let crate_root =
-                            self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
-                        self.populate_module_if_necessary(crate_root);
-                        Some(crate_root)
-                    } else {
-                        warn_if_binding_comes_from_local_crate = true;
-                        None
-                    };
-
-                    if let Some(crate_root) = crate_root {
-                        let binding = (crate_root, ty::Visibility::Public, directive.span,
-                                       directive.expansion).to_name_binding(self.arenas);
-                        let binding = self.arenas.alloc_name_binding(NameBinding {
-                            kind: NameBindingKind::Import {
-                                binding,
-                                directive,
-                                used: Cell::new(false),
-                            },
-                            vis: directive.vis.get(),
-                            span: directive.span,
-                            expansion: directive.expansion,
-                        });
-                        let _ = self.try_define(directive.parent, target, TypeNS, binding);
-                        let import = self.import_map.entry(directive.id).or_default();
-                        import[TypeNS] = Some(PathResolution::new(binding.def()));
-                        return None;
-                    }
-                }
-                _ => {}
-            }
-        }
 
         let module_result = self.resolve_path(
+            Some(ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name())),
             &module_path,
             None,
             true,
@@ -720,7 +751,7 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa
                 if !self_path.is_empty() && !is_special(self_path[0]) &&
                    !(self_path.len() > 1 && is_special(self_path[1])) {
                     self_path[0].name = keywords::SelfValue.name();
-                    self_result = Some(self.resolve_path(&self_path, None, false,
+                    self_result = Some(self.resolve_path(None, &self_path, None, false,
                                                          span, CrateLint::No));
                 }
                 return if let Some(PathResult::Module(..)) = self_result {
@@ -734,12 +765,27 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa
 
         let (ident, result, type_ns_only) = match directive.subclass {
             SingleImport { source, ref result, type_ns_only, .. } => (source, result, type_ns_only),
-            GlobImport { .. } if module.def_id() == directive.parent.def_id() => {
-                // Importing a module into itself is not allowed.
-                return Some((directive.span,
-                             "Cannot glob-import a module into itself.".to_string()));
-            }
             GlobImport { is_prelude, ref max_vis } => {
+                if module_path.len() <= 1 {
+                    // HACK(eddyb) `lint_if_path_starts_with_module` needs at least
+                    // 2 segments, so the `resolve_path` above won't trigger it.
+                    let mut full_path = module_path.clone();
+                    full_path.push(keywords::Invalid.ident());
+                    self.lint_if_path_starts_with_module(
+                        directive.crate_lint(),
+                        &full_path,
+                        directive.span,
+                        None,
+                    );
+                }
+
+                if let ModuleOrUniformRoot::Module(module) = module {
+                    if module.def_id() == directive.parent.def_id() {
+                        // Importing a module into itself is not allowed.
+                        return Some((directive.span,
+                            "Cannot glob-import a module into itself.".to_string()));
+                    }
+                }
                 if !is_prelude &&
                    max_vis.get() != ty::Visibility::Invisible && // Allow empty globs.
                    !max_vis.get().is_at_least(directive.vis.get(), &*self) {
@@ -756,8 +802,10 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa
             if let Ok(binding) = result[ns].get() {
                 all_ns_err = false;
                 if this.record_use(ident, ns, binding, directive.span) {
-                    this.resolution(module, ident, ns).borrow_mut().binding =
-                        Some(this.dummy_binding);
+                    if let ModuleOrUniformRoot::Module(module) = module {
+                        this.resolution(module, ident, ns).borrow_mut().binding =
+                            Some(this.dummy_binding);
+                    }
                 }
             }
         });
@@ -772,8 +820,13 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa
             });
 
             return if all_ns_failed {
-                let resolutions = module.resolutions.borrow();
-                let names = resolutions.iter().filter_map(|(&(ref i, _), resolution)| {
+                let resolutions = match module {
+                    ModuleOrUniformRoot::Module(module) =>
+                        Some(module.resolutions.borrow()),
+                    ModuleOrUniformRoot::UniformRoot(_) => None,
+                };
+                let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
+                let names = resolutions.filter_map(|(&(ref i, _), resolution)| {
                     if *i == ident { return None; } // Never suggest the same name
                     match *resolution.borrow() {
                         NameResolution { binding: Some(name_binding), .. } => {
@@ -799,11 +852,24 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa
                         Some(name) => format!(". Did you mean to use `{}`?", name),
                         None => "".to_owned(),
                     };
-                let module_str = module_to_string(module);
-                let msg = if let Some(module_str) = module_str {
-                    format!("no `{}` in `{}`{}", ident, module_str, lev_suggestion)
-                } else {
-                    format!("no `{}` in the root{}", ident, lev_suggestion)
+                let msg = match module {
+                    ModuleOrUniformRoot::Module(module) => {
+                        let module_str = module_to_string(module);
+                        if let Some(module_str) = module_str {
+                            format!("no `{}` in `{}`{}", ident, module_str, lev_suggestion)
+                        } else {
+                            format!("no `{}` in the root{}", ident, lev_suggestion)
+                        }
+                    }
+                    ModuleOrUniformRoot::UniformRoot(_) => {
+                        if !ident.is_path_segment_keyword() {
+                            format!("no `{}` external crate{}", ident, lev_suggestion)
+                        } else {
+                            // HACK(eddyb) this shows up for `self` & `super`, which
+                            // should work instead - for now keep the same error message.
+                            format!("no `{}` in the root{}", ident, lev_suggestion)
+                        }
+                    }
                 };
                 Some((span, msg))
             } else {
@@ -854,26 +920,20 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa
             }
         }
 
-        if warn_if_binding_comes_from_local_crate {
-            let mut warned = false;
+        if module_path.len() <= 1 {
+            // HACK(eddyb) `lint_if_path_starts_with_module` needs at least
+            // 2 segments, so the `resolve_path` above won't trigger it.
+            let mut full_path = module_path.clone();
+            full_path.push(ident);
             self.per_ns(|this, ns| {
-                let binding = match result[ns].get().ok() {
-                    Some(b) => b,
-                    None => return
-                };
-                if let NameBindingKind::Import { directive: d, .. } = binding.kind {
-                    if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass {
-                        return
-                    }
-                }
-                if warned {
-                    return
+                if let Ok(binding) = result[ns].get() {
+                    this.lint_if_path_starts_with_module(
+                        directive.crate_lint(),
+                        &full_path,
+                        directive.span,
+                        Some(binding),
+                    );
                 }
-                warned = true;
-                this.lint_path_starts_with_module(
-                    directive.root_id,
-                    directive.root_span,
-                );
             });
         }
 
@@ -890,7 +950,15 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa
     }
 
     fn resolve_glob_import(&mut self, directive: &'b ImportDirective<'b>) {
-        let module = directive.imported_module.get().unwrap();
+        let module = match directive.imported_module.get().unwrap() {
+            ModuleOrUniformRoot::Module(module) => module,
+            ModuleOrUniformRoot::UniformRoot(_) => {
+                self.session.span_err(directive.span,
+                    "cannot glob-import all possible crates");
+                return;
+            }
+        };
+
         self.populate_module_if_necessary(module);
 
         if let Some(Def::Trait(_)) = module.def() {
@@ -1012,8 +1080,10 @@ fn finalize_resolutions_in(&mut self, module: Module<'b>) {
                             };
                             let mut err = self.session.struct_span_err(binding.span, &msg);
 
-                            let imported_module = directive.imported_module.get()
-                                .expect("module should exist");
+                            let imported_module = match directive.imported_module.get() {
+                                Some(ModuleOrUniformRoot::Module(module)) => module,
+                                _ => bug!("module should exist"),
+                            };
                             let resolutions = imported_module.parent.expect("parent should exist")
                                 .resolutions.borrow();
                             let enum_path_segment_index = directive.module_path.len() - 1;