about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJeffrey Seyfried <jeffrey.seyfried@gmail.com>2016-11-25 06:07:21 +0000
committerJeffrey Seyfried <jeffrey.seyfried@gmail.com>2016-11-29 00:18:34 +0000
commitaf2d89c7f64a590b5c9e8445fc03716cf0dcbca1 (patch)
treeafd46a63dba33aa05afc924ab4e6993dc0d0591c
parentcb9f14e987732fa1406911633d9bf4e7eee45978 (diff)
downloadrust-af2d89c7f64a590b5c9e8445fc03716cf0dcbca1.tar.gz
rust-af2d89c7f64a590b5c9e8445fc03716cf0dcbca1.zip
Refactor path resoloution.
-rw-r--r--src/librustc/hir/lowering.rs4
-rw-r--r--src/librustc_resolve/lib.rs793
-rw-r--r--src/librustc_resolve/resolve_imports.rs34
-rw-r--r--src/test/compile-fail/issue-28388-1.rs2
4 files changed, 287 insertions, 546 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 39d0ed9a67b..d113f6b5e4e 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -77,7 +77,7 @@ pub struct LoweringContext<'a> {
 
 pub trait Resolver {
     // Resolve a global hir path generated by the lowerer when expanding `for`, `if let`, etc.
-    fn resolve_generated_global_path(&mut self, path: &mut hir::Path, is_value: bool);
+    fn resolve_hir_path(&mut self, path: &mut hir::Path, is_value: bool);
 
     // Obtain the resolution for a node id
     fn get_resolution(&mut self, id: NodeId) -> Option<PathResolution>;
@@ -2091,7 +2091,7 @@ impl<'a> LoweringContext<'a> {
             segments: segments.into(),
         };
 
-        self.resolver.resolve_generated_global_path(&mut path, is_value);
+        self.resolver.resolve_hir_path(&mut path, is_value);
         path
     }
 
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 7b73582449b..19764f2e682 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -39,8 +39,6 @@ use self::ResolveResult::*;
 use self::FallbackSuggestion::*;
 use self::TypeParameters::*;
 use self::RibKind::*;
-use self::UseLexicalScopeFlag::*;
-use self::ModulePrefixResult::*;
 
 use rustc::hir::map::{Definitions, DefCollector};
 use rustc::hir::{self, PrimTy, TyBool, TyChar, TyFloat, TyInt, TyUint, TyStr};
@@ -57,6 +55,7 @@ use syntax::ext::hygiene::{Mark, SyntaxContext};
 use syntax::ast::{self, FloatTy};
 use syntax::ast::{CRATE_NODE_ID, Name, NodeId, Ident, SpannedIdent, IntTy, UintTy};
 use syntax::ext::base::SyntaxExtension;
+use syntax::ext::base::Determinacy::{Determined, Undetermined};
 use syntax::symbol::{Symbol, keywords};
 use syntax::util::lev_distance::find_best_match_for_name;
 
@@ -191,10 +190,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
                                         span: syntax_pos::Span,
                                         resolution_error: ResolutionError<'c>)
                                         -> DiagnosticBuilder<'a> {
-    if !resolver.emit_errors {
-        return resolver.session.diagnostic().struct_dummy();
-    }
-
     match resolution_error {
         ResolutionError::TypeParametersFromOuterFunction => {
             let mut err = struct_span_err!(resolver.session,
@@ -584,8 +579,9 @@ impl<'a> Visitor for Resolver<'a> {
         self.resolve_type(ty);
     }
     fn visit_poly_trait_ref(&mut self, tref: &ast::PolyTraitRef, m: &ast::TraitBoundModifier) {
-        let def =
-            self.resolve_trait_reference(tref.trait_ref.ref_id, &tref.trait_ref.path, 0, None);
+        let ast::Path { ref segments, span, global } = tref.trait_ref.path;
+        let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect();
+        let def = self.resolve_trait_reference(&path, global, None, span);
         self.record_def(tref.trait_ref.ref_id, def);
         visit::walk_poly_trait_ref(self, tref, m);
     }
@@ -737,17 +733,6 @@ enum RibKind<'a> {
     MacroDefinition(Mark),
 }
 
-#[derive(Copy, Clone)]
-enum UseLexicalScopeFlag {
-    DontUseLexicalScope,
-    UseLexicalScope,
-}
-
-enum ModulePrefixResult<'a> {
-    NoPrefixFound,
-    PrefixFound(Module<'a>, usize),
-}
-
 /// One local scope.
 #[derive(Debug)]
 struct Rib<'a> {
@@ -765,33 +750,18 @@ impl<'a> Rib<'a> {
 }
 
 /// A definition along with the index of the rib it was found on
+#[derive(Copy, Clone)]
 struct LocalDef {
     ribs: Option<(Namespace, usize)>,
     def: Def,
 }
 
-impl LocalDef {
-    fn from_def(def: Def) -> Self {
-        LocalDef {
-            ribs: None,
-            def: def,
-        }
-    }
-}
-
 enum LexicalScopeBinding<'a> {
     Item(&'a NameBinding<'a>),
-    LocalDef(LocalDef),
+    Def(Def),
 }
 
 impl<'a> LexicalScopeBinding<'a> {
-    fn local_def(self) -> LocalDef {
-        match self {
-            LexicalScopeBinding::LocalDef(local_def) => local_def,
-            LexicalScopeBinding::Item(binding) => LocalDef::from_def(binding.def()),
-        }
-    }
-
     fn item(self) -> Option<&'a NameBinding<'a>> {
         match self {
             LexicalScopeBinding::Item(binding) => Some(binding),
@@ -800,6 +770,21 @@ impl<'a> LexicalScopeBinding<'a> {
     }
 }
 
+#[derive(Copy, Clone)]
+enum PathScope {
+    Global,
+    Lexical,
+    Import,
+}
+
+#[derive(Clone)]
+enum PathResult<'a> {
+    Module(Module<'a>),
+    NonModule(PathResolution),
+    Indeterminate,
+    Failed(String, bool /* is the error from the last segment? */),
+}
+
 enum ModuleKind {
     Block(NodeId),
     Def(Def, Name),
@@ -1107,11 +1092,6 @@ pub struct Resolver<'a> {
     module_map: NodeMap<Module<'a>>,
     extern_crate_roots: FxHashMap<(CrateNum, bool /* MacrosOnly? */), Module<'a>>,
 
-    // Whether or not to print error messages. Can be set to true
-    // when getting additional info for error message suggestions,
-    // so as to avoid printing duplicate errors
-    emit_errors: bool,
-
     pub make_glob_map: bool,
     // Maps imports to the names of items actually imported (this actually maps
     // all imports, but only glob imports are actually interesting).
@@ -1197,22 +1177,23 @@ impl<'a> ty::NodeIdTree for Resolver<'a> {
 }
 
 impl<'a> hir::lowering::Resolver for Resolver<'a> {
-    fn resolve_generated_global_path(&mut self, path: &mut hir::Path, is_value: bool) {
+    fn resolve_hir_path(&mut self, path: &mut hir::Path, is_value: bool) {
         let namespace = if is_value { ValueNS } else { TypeNS };
-        match self.resolve_crate_relative_path(path.span, &path.segments, namespace) {
-            Ok(binding) => path.def = binding.def(),
-            None => {
-                let path_name = &format!("{}", path);
-                let error =
-                    ResolutionError::UnresolvedName {
-                        path: path_name,
-                        message: "",
-                        context: UnresolvedNameContext::Other,
-                        is_static_method: false,
-                        is_field: false,
-                        def: Def::Err,
-                    };
-                resolve_error(self, path.span, error);
+        let hir::Path { ref segments, span, global, ref mut def } = *path;
+        let path: Vec<_> = segments.iter().map(|seg| Ident::with_empty_ctxt(seg.name)).collect();
+        let scope = if global { PathScope::Global } else { PathScope::Lexical };
+        match self.resolve_path(&path, scope, Some(namespace), Some(span)) {
+            PathResult::Module(module) => *def = module.def().unwrap(),
+            PathResult::NonModule(path_res) if path_res.depth == 0 => *def = path_res.base_def,
+            PathResult::NonModule(..) => match self.resolve_path(&path, scope, None, Some(span)) {
+                PathResult::Failed(msg, _) => {
+                    resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
+                }
+                _ => {}
+            },
+            PathResult::Indeterminate => unreachable!(),
+            PathResult::Failed(msg, _) => {
+                resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
             }
         }
     }
@@ -1230,22 +1211,6 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> {
     }
 }
 
-trait Named {
-    fn ident(&self) -> Ident;
-}
-
-impl Named for ast::PathSegment {
-    fn ident(&self) -> Ident {
-        self.identifier
-    }
-}
-
-impl Named for hir::PathSegment {
-    fn ident(&self) -> Ident {
-        Ident::with_empty_ctxt(self.name)
-    }
-}
-
 impl<'a> Resolver<'a> {
     pub fn new(session: &'a Session,
                krate: &Crate,
@@ -1307,7 +1272,6 @@ impl<'a> Resolver<'a> {
             module_map: module_map,
             extern_crate_roots: FxHashMap(),
 
-            emit_errors: true,
             make_glob_map: make_glob_map == MakeGlobMap::Yes,
             glob_map: NodeMap(),
 
@@ -1413,163 +1377,6 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    fn expect_module(&mut self, name: Name, binding: &'a NameBinding<'a>, span: Option<Span>)
-                     -> ResolveResult<Module<'a>> {
-        match binding.module() {
-            Ok(module) => Success(module),
-            Err(true) => Failed(None),
-            Err(false) => {
-                let msg = format!("Not a module `{}`", name);
-                Failed(span.map(|span| (span, msg)))
-            }
-        }
-    }
-
-    /// Resolves the given module path from the given root `search_module`.
-    fn resolve_module_path_from_root(&mut self,
-                                     mut search_module: Module<'a>,
-                                     module_path: &[Ident],
-                                     index: usize,
-                                     span: Option<Span>)
-                                     -> ResolveResult<Module<'a>> {
-        fn search_parent_externals<'a>(this: &mut Resolver<'a>, needle: Name, module: Module<'a>)
-                                       -> Option<Module<'a>> {
-            match this.resolve_name_in_module(module, needle, TypeNS, false, None) {
-                Success(binding) if binding.is_extern_crate() => Some(module),
-                _ => if let (&ModuleKind::Def(..), Some(parent)) = (&module.kind, module.parent) {
-                    search_parent_externals(this, needle, parent)
-                } else {
-                    None
-                },
-            }
-        }
-
-        let mut index = index;
-        let module_path_len = module_path.len();
-
-        // Resolve the module part of the path. This does not involve looking
-        // upward though scope chains; we simply resolve names directly in
-        // modules as we go.
-        while index < module_path_len {
-            let name = module_path[index].name;
-            match self.resolve_name_in_module(search_module, name, TypeNS, false, span) {
-                Failed(_) => {
-                    let module_name = module_to_string(search_module);
-                    let msg = if "???" == &module_name {
-                        let current_module = self.current_module;
-                        match search_parent_externals(self, name, current_module) {
-                            Some(module) => {
-                                let path_str = names_to_string(module_path);
-                                let target_mod_str = module_to_string(&module);
-                                let current_mod_str = module_to_string(current_module);
-
-                                let prefix = if target_mod_str == current_mod_str {
-                                    "self::".to_string()
-                                } else {
-                                    format!("{}::", target_mod_str)
-                                };
-
-                                format!("Did you mean `{}{}`?", prefix, path_str)
-                            }
-                            None => format!("Maybe a missing `extern crate {};`?", name),
-                        }
-                    } else {
-                        format!("Could not find `{}` in `{}`", name, module_name)
-                    };
-
-                    return Failed(span.map(|span| (span, msg)));
-                }
-                Indeterminate => {
-                    debug!("(resolving module path for import) module resolution is \
-                            indeterminate: {}",
-                           name);
-                    return Indeterminate;
-                }
-                Success(binding) => {
-                    // Check to see whether there are type bindings, and, if
-                    // so, whether there is a module within.
-                    match self.expect_module(name, binding, span) {
-                        Success(module) => search_module = module,
-                        result @ _ => return result,
-                    }
-                }
-            }
-
-            index += 1;
-        }
-
-        return Success(search_module);
-    }
-
-    /// Attempts to resolve the module part of an import directive or path
-    /// rooted at the given module.
-    fn resolve_module_path(&mut self,
-                           module_path: &[Ident],
-                           use_lexical_scope: UseLexicalScopeFlag,
-                           span: Option<Span>)
-                           -> ResolveResult<Module<'a>> {
-        if module_path.len() == 0 {
-            return Success(self.graph_root) // Use the crate root
-        }
-
-        debug!("(resolving module path for import) processing `{}` rooted at `{}`",
-               names_to_string(module_path),
-               module_to_string(self.current_module));
-
-        // Resolve the module prefix, if any.
-        let module_prefix_result = self.resolve_module_prefix(module_path, span);
-
-        let search_module;
-        let start_index;
-        match module_prefix_result {
-            Failed(err) => return Failed(err),
-            Indeterminate => {
-                debug!("(resolving module path for import) indeterminate; bailing");
-                return Indeterminate;
-            }
-            Success(NoPrefixFound) => {
-                // There was no prefix, so we're considering the first element
-                // of the path. How we handle this depends on whether we were
-                // instructed to use lexical scope or not.
-                match use_lexical_scope {
-                    DontUseLexicalScope => {
-                        // This is a crate-relative path. We will start the
-                        // resolution process at index zero.
-                        search_module = self.graph_root;
-                        start_index = 0;
-                    }
-                    UseLexicalScope => {
-                        // This is not a crate-relative path. We resolve the
-                        // first component of the path in the current lexical
-                        // scope and then proceed to resolve below that.
-                        let ident = module_path[0];
-                        let lexical_binding =
-                            self.resolve_ident_in_lexical_scope(ident, TypeNS, span);
-                        if let Some(binding) = lexical_binding.and_then(LexicalScopeBinding::item) {
-                            match self.expect_module(ident.name, binding, span) {
-                                Success(containing_module) => {
-                                    search_module = containing_module;
-                                    start_index = 1;
-                                }
-                                result @ _ => return result,
-                            }
-                        } else {
-                            let msg =
-                                format!("Use of undeclared type or module `{}`", ident.name);
-                            return Failed(span.map(|span| (span, msg)));
-                        }
-                    }
-                }
-            }
-            Success(PrefixFound(ref containing_module, index)) => {
-                search_module = containing_module;
-                start_index = index;
-            }
-        }
-
-        self.resolve_module_path_from_root(search_module, module_path, start_index, span)
-    }
-
     /// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope.
     /// More specifically, we proceed up the hierarchy of scopes and return the binding for
     /// `ident` in the first scope that defines it (or None if no scopes define it).
@@ -1600,9 +1407,10 @@ impl<'a> Resolver<'a> {
         for i in (0 .. self.ribs[ns].len()).rev() {
             if let Some(def) = self.ribs[ns][i].bindings.get(&ident).cloned() {
                 // The ident resolves to a type parameter or local variable.
-                return Some(LexicalScopeBinding::LocalDef(LocalDef {
-                    ribs: Some((ns, i)),
-                    def: def,
+                return Some(LexicalScopeBinding::Def(if let Some(span) = record_used {
+                    self.adjust_local_def(LocalDef { ribs: Some((ns, i)), def: def }, span)
+                } else {
+                    def
                 }));
             }
 
@@ -1637,45 +1445,6 @@ impl<'a> Resolver<'a> {
         None
     }
 
-    /// Resolves a "module prefix". A module prefix is one or both of (a) `self::`;
-    /// (b) some chain of `super::`.
-    /// grammar: (SELF MOD_SEP ) ? (SUPER MOD_SEP) *
-    fn resolve_module_prefix(&mut self, module_path: &[Ident], span: Option<Span>)
-                             -> ResolveResult<ModulePrefixResult<'a>> {
-        if module_path[0].name == "$crate" {
-            return Success(PrefixFound(self.resolve_crate_var(module_path[0].ctxt), 1));
-        }
-
-        // Start at the current module if we see `self` or `super`, or at the
-        // top of the crate otherwise.
-        let mut i = match &*module_path[0].name.as_str() {
-            "self" => 1,
-            "super" => 0,
-            _ => return Success(NoPrefixFound),
-        };
-
-        let mut containing_module =
-            self.module_map[&self.current_module.normal_ancestor_id.unwrap()];
-
-        // Now loop through all the `super`s we find.
-        while i < module_path.len() && module_path[i].name == "super" {
-            debug!("(resolving module prefix) resolving `super` at {}",
-                   module_to_string(&containing_module));
-            if let Some(parent) = containing_module.parent {
-                containing_module = self.module_map[&parent.normal_ancestor_id.unwrap()];
-                i += 1;
-            } else {
-                let msg = "There are too many initial `super`s.".into();
-                return Failed(span.map(|span| (span, msg)));
-            }
-        }
-
-        debug!("(resolving module prefix) finished resolving prefix at {}",
-               module_to_string(&containing_module));
-
-        return Success(PrefixFound(containing_module, i));
-    }
-
     fn resolve_crate_var(&mut self, mut crate_var_ctxt: SyntaxContext) -> Module<'a> {
         while crate_var_ctxt.source().0 != SyntaxContext::empty() {
             crate_var_ctxt = crate_var_ctxt.source().0;
@@ -1834,23 +1603,32 @@ impl<'a> Resolver<'a> {
             ItemKind::Use(ref view_path) => {
                 match view_path.node {
                     ast::ViewPathList(ref prefix, ref items) => {
+                        let path: Vec<_> =
+                            prefix.segments.iter().map(|seg| seg.identifier).collect();
                         // Resolve prefix of an import with empty braces (issue #28388)
                         if items.is_empty() && !prefix.segments.is_empty() {
-                            match self.resolve_crate_relative_path(prefix.span,
-                                                                   &prefix.segments,
-                                                                   TypeNS) {
-                                Some(binding) => {
-                                    let def = binding.def();
-                                    self.record_def(item.id, PathResolution::new(def));
-                                }
-                                None => {
-                                    resolve_error(self,
-                                                  prefix.span,
-                                                  ResolutionError::FailedToResolve(
-                                                      &path_names_to_string(prefix, 0)));
-                                    self.record_def(item.id, err_path_resolution());
+                            let (scope, span) = (PathScope::Import, prefix.span);
+                            // FIXME(#38012) This should be a module path, not anything in TypeNS.
+                            let result =
+                                self.resolve_path(&path, scope, Some(TypeNS), Some(span));
+                            let (def, msg) = match result {
+                                PathResult::Module(module) => (module.def().unwrap(), None),
+                                PathResult::NonModule(res) if res.depth == 0 =>
+                                    (res.base_def, None),
+                                PathResult::NonModule(_) => {
+                                    // Resolve a module path for better errors
+                                    match self.resolve_path(&path, scope, None, Some(span)) {
+                                        PathResult::Failed(msg, _) => (Def::Err, Some(msg)),
+                                        _ => unreachable!(),
+                                    }
                                 }
+                                PathResult::Indeterminate => unreachable!(),
+                                PathResult::Failed(msg, _) => (Def::Err, Some(msg)),
+                            };
+                            if let Some(msg) = msg {
+                                resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
                             }
+                            self.record_def(item.id, PathResolution::new(def));
                         }
                     }
                     _ => {}
@@ -1925,60 +1703,51 @@ impl<'a> Resolver<'a> {
     }
 
     fn resolve_trait_reference(&mut self,
-                               id: NodeId,
-                               trait_path: &Path,
-                               path_depth: usize,
-                               generics: Option<&Generics>)
+                               path: &[Ident],
+                               global: bool,
+                               generics: Option<&Generics>,
+                               span: Span)
                                -> PathResolution {
-        if let Some(path_res) = self.resolve_path(id, trait_path, path_depth, TypeNS) {
-            match path_res.base_def {
-                Def::Trait(_) => {
-                    debug!("(resolving trait) found trait def: {:?}", path_res);
-                    return path_res;
-                }
-                Def::Err => return err_path_resolution(),
-                _ => {}
+        let scope = if global { PathScope::Global } else { PathScope::Lexical };
+        let def = match self.resolve_path(path, scope, None, Some(span)) {
+            PathResult::Module(module) => Some(module.def().unwrap()),
+            PathResult::NonModule(..) => return err_path_resolution(),
+            PathResult::Failed(msg, false) => {
+                resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
+                return err_path_resolution();
+            }
+            _ => match self.resolve_path(path, scope, Some(TypeNS), None) {
+                PathResult::NonModule(path_resolution) => Some(path_resolution.base_def),
+                _ => None,
+            },
+        };
+
+        if let Some(def) = def {
+            if let Def::Trait(_) = def {
+                return PathResolution::new(def);
             }
 
-            let mut err = resolve_struct_error(self, trait_path.span, {
-                ResolutionError::IsNotATrait(&path_names_to_string(trait_path, path_depth),
-                                             path_res.base_def.kind_name())
+            let mut err = resolve_struct_error(self, span, {
+                ResolutionError::IsNotATrait(&names_to_string(path), def.kind_name())
             });
             if let Some(generics) = generics {
-                if let Some(span) = generics.span_for_name(
-                    &path_names_to_string(trait_path, path_depth)) {
-
+                if let Some(span) = generics.span_for_name(&names_to_string(path)) {
                     err.span_label(span, &"type parameter defined here");
                 }
             }
 
             // If it's a typedef, give a note
-            if let Def::TyAlias(..) = path_res.base_def {
+            if let Def::TyAlias(..) = def {
                 err.note(&format!("type aliases cannot be used for traits"));
             }
             err.emit();
         } else {
             // find possible candidates
-            let trait_name = trait_path.segments.last().unwrap().identifier.name;
-            let candidates =
-                self.lookup_candidates(
-                    trait_name,
-                    TypeNS,
-                    |def| match def {
-                        Def::Trait(_) => true,
-                        _             => false,
-                    },
-                );
-
-            // create error object
-            let name = &path_names_to_string(trait_path, path_depth);
-            let error =
-                ResolutionError::UndeclaredTraitName(
-                    name,
-                    candidates,
-                );
+            let is_trait = |def| match def { Def::Trait(_) => true, _ => false };
+            let candidates = self.lookup_candidates(path.last().unwrap().name, TypeNS, is_trait);
 
-            resolve_error(self, trait_path.span, error);
+            let path = names_to_string(path);
+            resolve_error(self, span, ResolutionError::UndeclaredTraitName(&path, candidates));
         }
         err_path_resolution()
     }
@@ -2003,8 +1772,9 @@ impl<'a> Resolver<'a> {
         let mut new_val = None;
         let mut new_id = None;
         if let Some(trait_ref) = opt_trait_ref {
-            let path_res =
-                self.resolve_trait_reference(trait_ref.ref_id, &trait_ref.path, 0, generics);
+            let ast::Path { ref segments, span, global } = trait_ref.path;
+            let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect();
+            let path_res = self.resolve_trait_reference(&path, global, generics, span);
             assert!(path_res.depth == 0);
             self.record_def(trait_ref.ref_id, path_res);
             if path_res.base_def != Def::Err {
@@ -2265,8 +2035,7 @@ impl<'a> Resolver<'a> {
                     self.record_def(ty.id, err_path_resolution());
 
                     // Keep reporting some errors even if they're ignored above.
-                    let result = self.resolve_path(ty.id, path, 0, TypeNS);
-                    if result.map(|resolution| resolution.base_def) != Some(Def::Err) {
+                    {
                         let kind = if maybe_qself.is_some() {
                             "associated type"
                         } else {
@@ -2408,13 +2177,8 @@ impl<'a> Resolver<'a> {
                 resolution
             }
         } else {
-            if self.resolve_path(pat_id, path, 0, namespace).is_none() {
-                resolve_error(
-                    self,
-                    path.span,
-                    ResolutionError::PatPathUnresolved(expected_what, path)
-                );
-            }
+            let error = ResolutionError::PatPathUnresolved(expected_what, path);
+            resolve_error(self, path.span, error);
             err_path_resolution()
         };
 
@@ -2526,81 +2290,32 @@ impl<'a> Resolver<'a> {
                                    id: NodeId,
                                    maybe_qself: Option<&QSelf>,
                                    path: &Path,
-                                   namespace: Namespace)
+                                   ns: Namespace)
                                    -> Option<PathResolution> {
-        let max_assoc_types;
-
-        match maybe_qself {
-            Some(qself) => {
-                if qself.position == 0 {
-                    // FIXME: Create some fake resolution that can't possibly be a type.
-                    return Some(PathResolution {
-                        base_def: Def::Mod(self.definitions.local_def_id(ast::CRATE_NODE_ID)),
-                        depth: path.segments.len(),
-                    });
-                }
-                max_assoc_types = path.segments.len() - qself.position;
-                // Make sure the trait is valid.
-                self.resolve_trait_reference(id, path, max_assoc_types, None);
-            }
-            None => {
-                max_assoc_types = path.segments.len();
-            }
-        }
-
-        let mut resolution = self.with_no_errors(|this| {
-            this.resolve_path(id, path, 0, namespace)
-        });
-        if resolution.map(|res| res.base_def) == Some(Def::Err) { resolution = None; }
-        for depth in 1..max_assoc_types {
-            if resolution.is_some() {
-                break;
+        let ast::Path { ref segments, global, span } = *path;
+        let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect();
+        let scope = if global { PathScope::Global } else { PathScope::Lexical };
+
+        if let Some(qself) = maybe_qself {
+            if qself.position == 0 {
+                // FIXME: Create some fake resolution that can't possibly be a type.
+                return Some(PathResolution {
+                    base_def: Def::Mod(self.definitions.local_def_id(ast::CRATE_NODE_ID)),
+                    depth: path.len(),
+                });
             }
-            self.with_no_errors(|this| {
-                let partial_resolution = this.resolve_path(id, path, depth, TypeNS);
-                if let Some(Def::Mod(..)) = partial_resolution.map(|r| r.base_def) {
-                    // Modules cannot have associated items
-                } else {
-                    resolution = partial_resolution;
-                    if resolution.map(|res| res.base_def) == Some(Def::Err) { resolution = None; }
-                }
-            });
+            // Make sure the trait is valid.
+            self.resolve_trait_reference(&path[..qself.position], global, None, span);
         }
-        resolution
-    }
-
-    /// Skips `path_depth` trailing segments, which is also reflected in the
-    /// returned value. See `hir::def::PathResolution` for more info.
-    fn resolve_path(&mut self, id: NodeId, path: &Path, path_depth: usize, namespace: Namespace)
-                    -> Option<PathResolution> {
-        debug!("resolve_path(id={:?} path={:?}, path_depth={:?})", id, path, path_depth);
 
-        let span = path.span;
-        let segments = &path.segments[..path.segments.len() - path_depth];
-
-        let mk_res = |def| PathResolution { base_def: def, depth: path_depth };
-
-        if path.global {
-            let binding = self.resolve_crate_relative_path(span, segments, namespace);
-            return binding.map(|binding| mk_res(binding.def()));
-        }
-
-        // Try to find a path to an item in a module.
-        let last_ident = segments.last().unwrap().identifier;
-        // Resolve a single identifier with fallback to primitive types
-        let resolve_identifier_with_fallback = |this: &mut Self, record_used| {
-            let def = this.resolve_identifier(last_ident, namespace, record_used);
-            match def {
-                None | Some(LocalDef{def: Def::Mod(..), ..}) if namespace == TypeNS =>
-                    this.primitive_type_table
-                        .primitive_types
-                        .get(&last_ident.name)
-                        .map_or(def, |prim_ty| Some(LocalDef::from_def(Def::PrimTy(*prim_ty)))),
-                _ => def
+        let result = match self.resolve_path(&path, scope, Some(ns), Some(span)) {
+            PathResult::NonModule(path_res) => match path_res.base_def {
+                Def::Trait(..) if maybe_qself.is_some() => return None,
+                _ => path_res,
+            },
+            PathResult::Module(module) if !module.is_normal() => {
+                PathResolution::new(module.def().unwrap())
             }
-        };
-
-        if segments.len() == 1 {
             // In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we
             // don't report an error right away, but try to fallback to a primitive type.
             // So, we are still able to successfully resolve something like
@@ -2613,39 +2328,140 @@ impl<'a> Resolver<'a> {
             //
             // Such behavior is required for backward compatibility.
             // The same fallback is used when `a` resolves to nothing.
-            let def = resolve_identifier_with_fallback(self, Some(span));
-            return def.map(|def| mk_res(self.adjust_local_def(def, span)));
+            _ if self.primitive_type_table.primitive_types.contains_key(&path[0].name) => {
+                PathResolution {
+                    base_def: Def::PrimTy(self.primitive_type_table.primitive_types[&path[0].name]),
+                    depth: segments.len() - 1,
+                }
+            }
+            PathResult::Module(module) => PathResolution::new(module.def().unwrap()),
+            PathResult::Failed(msg, false) => {
+                resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
+                err_path_resolution()
+            }
+            _ => return None,
+        };
+
+        if path.len() == 1 || result.base_def == Def::Err {
+            return Some(result);
         }
 
-        let unqualified_def = resolve_identifier_with_fallback(self, None);
-        let qualified_binding = self.resolve_module_relative_path(span, segments, namespace);
-        match (qualified_binding, unqualified_def) {
-            (Some(binding), Some(ref ud)) if binding.def() == ud.def &&
-                                             segments[0].identifier.name != "$crate" => {
-                self.session
-                    .add_lint(lint::builtin::UNUSED_QUALIFICATIONS,
-                              id,
-                              span,
-                              "unnecessary qualification".to_string());
+        let unqualified_result = {
+            match self.resolve_path(&[*path.last().unwrap()], PathScope::Lexical, Some(ns), None) {
+                PathResult::NonModule(path_res) => path_res.base_def,
+                PathResult::Module(module) => module.def().unwrap(),
+                _ => return Some(result),
             }
-            _ => {}
+        };
+        if result.base_def == unqualified_result && path[0].name != "$crate" {
+            let lint = lint::builtin::UNUSED_QUALIFICATIONS;
+            self.session.add_lint(lint, id, span, "unnecessary qualification".to_string());
         }
 
-        qualified_binding.map(|binding| mk_res(binding.def()))
+        Some(result)
     }
 
-    // Resolve a single identifier
-    fn resolve_identifier(&mut self,
-                          identifier: Ident,
-                          namespace: Namespace,
-                          record_used: Option<Span>)
-                          -> Option<LocalDef> {
-        if identifier.name == keywords::Invalid.name() {
-            return None;
+    fn resolve_path(&mut self,
+                    path: &[Ident],
+                    scope: PathScope,
+                    opt_ns: Option<Namespace>, // `None` indicates a module path
+                    record_used: Option<Span>)
+                    -> PathResult<'a> {
+        let (mut module, allow_self) = match scope {
+            PathScope::Lexical => (None, true),
+            PathScope::Import => (Some(self.graph_root), true),
+            PathScope::Global => (Some(self.graph_root), false),
+        };
+        let mut allow_super = allow_self;
+
+        for (i, &ident) in path.iter().enumerate() {
+            let is_last = i == path.len() - 1;
+            let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
+
+            if i == 0 && allow_self && ns == TypeNS && ident.name == keywords::SelfValue.name() {
+                module = Some(self.module_map[&self.current_module.normal_ancestor_id.unwrap()]);
+                continue
+            } else if i == 0 && allow_self && ns == TypeNS && ident.name == "$crate" {
+                module = Some(self.resolve_crate_var(ident.ctxt));
+                continue
+            } else if allow_super && ns == TypeNS && ident.name == keywords::Super.name() {
+                let current_module = if i == 0 { self.current_module } else { module.unwrap() };
+                let self_module = self.module_map[&current_module.normal_ancestor_id.unwrap()];
+                if let Some(parent) = self_module.parent {
+                    module = Some(self.module_map[&parent.normal_ancestor_id.unwrap()]);
+                    continue
+                } else {
+                    let msg = "There are too many initial `super`s.".to_string();
+                    return PathResult::Failed(msg, false);
+                }
+            }
+            allow_super = false;
+
+            let binding = if let Some(module) = module {
+                match self.resolve_name_in_module(module, ident.name, ns, false, record_used) {
+                    Success(binding) => Ok(binding),
+                    Indeterminate => Err(Undetermined),
+                    Failed(_) => Err(Determined),
+                }
+            } else {
+                match self.resolve_ident_in_lexical_scope(ident, ns, record_used) {
+                    Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
+                    Some(LexicalScopeBinding::Def(def)) if opt_ns.is_some() => {
+                        return PathResult::NonModule(PathResolution {
+                            base_def: def,
+                            depth: path.len() - 1,
+                        });
+                    }
+                    _ => Err(if record_used.is_some() { Determined } else { Undetermined }),
+                }
+            };
+
+            match binding {
+                Ok(binding) => {
+                    if let Ok(next_module) = binding.module() {
+                        module = Some(next_module);
+                    } else if binding.def() == Def::Err {
+                        return PathResult::NonModule(err_path_resolution());
+                    } else if opt_ns.is_some() {
+                        return PathResult::NonModule(PathResolution {
+                            base_def: binding.def(),
+                            depth: path.len() - i - 1,
+                        });
+                    } else {
+                        return PathResult::Failed(format!("Not a module `{}`", ident), is_last);
+                    }
+                }
+                Err(Undetermined) => return PathResult::Indeterminate,
+                Err(Determined) => {
+                    if let Some(module) = module {
+                        if opt_ns.is_some() && !module.is_normal() {
+                            return PathResult::NonModule(PathResolution {
+                                base_def: module.def().unwrap(),
+                                depth: path.len() - i,
+                            });
+                        }
+                    }
+                    let msg = if module.and_then(ModuleS::def) == self.graph_root.def() {
+                        let is_mod = |def| match def { Def::Mod(..) => true, _ => false };
+                        let mut candidates =
+                            self.lookup_candidates(ident.name, TypeNS, is_mod).candidates;
+                        candidates.sort_by_key(|path| (path.segments.len(), path.to_string()));
+                        if let Some(candidate) = candidates.get(0) {
+                            format!("Did you mean `{}`?", candidate)
+                        } else {
+                            format!("Maybe a missing `extern crate {};`?", ident)
+                        }
+                    } else if i == 0 {
+                        format!("Use of undeclared type or module `{}`", ident)
+                    } else {
+                        format!("Could not find `{}` in `{}`", ident, path[i - 1])
+                    };
+                    return PathResult::Failed(msg, is_last);
+                }
+            }
         }
 
-        self.resolve_ident_in_lexical_scope(identifier, namespace, record_used)
-            .map(LexicalScopeBinding::local_def)
+        PathResult::Module(module.unwrap())
     }
 
     // Resolve a local definition, potentially adjusting for closures.
@@ -2736,65 +2552,6 @@ impl<'a> Resolver<'a> {
         return def;
     }
 
-    // resolve a "module-relative" path, e.g. a::b::c
-    fn resolve_module_relative_path(&mut self,
-                                    span: Span,
-                                    segments: &[ast::PathSegment],
-                                    namespace: Namespace)
-                                    -> Option<&'a NameBinding<'a>> {
-        let module_path =
-            segments.split_last().unwrap().1.iter().map(|ps| ps.identifier).collect::<Vec<_>>();
-
-        let module = match self.resolve_module_path(&module_path, UseLexicalScope, Some(span)) {
-            Failed(err) => {
-                if let Some((span, msg)) = err {
-                    resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
-                }
-                return Some(self.dummy_binding);
-            }
-            Indeterminate => return None,
-            Success(module) => module,
-        };
-
-        let name = segments.last().unwrap().identifier.name;
-        self.resolve_name_in_module(module, name, namespace, false, Some(span)).success()
-    }
-
-    /// Invariant: This must be called only during main resolution, not during
-    /// import resolution.
-    fn resolve_crate_relative_path<T>(&mut self, span: Span, segments: &[T], namespace: Namespace)
-                                      -> Option<&'a NameBinding<'a>>
-        where T: Named,
-    {
-        let module_path = segments.split_last().unwrap().1.iter().map(T::ident).collect::<Vec<_>>();
-        let root = self.graph_root;
-
-        let module = match self.resolve_module_path_from_root(root, &module_path, 0, Some(span)) {
-            Failed(err) => {
-                if let Some((span, msg)) = err {
-                    resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
-                }
-                return Some(self.dummy_binding);
-            }
-
-            Indeterminate => return None,
-
-            Success(module) => module,
-        };
-
-        let name = segments.last().unwrap().ident().name;
-        self.resolve_name_in_module(module, name, namespace, false, Some(span)).success()
-    }
-
-    fn with_no_errors<T, F>(&mut self, f: F) -> T
-        where F: FnOnce(&mut Resolver) -> T
-    {
-        self.emit_errors = false;
-        let rs = f(self);
-        self.emit_errors = true;
-        rs
-    }
-
     // Calls `f` with a `Resolver` whose current lexical scope is `module`'s lexical scope,
     // i.e. the module's items and the prelude (unless the module is `#[no_implicit_prelude]`).
     // FIXME #34673: This needs testing.
@@ -2918,11 +2675,7 @@ impl<'a> Resolver<'a> {
 
                         let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?",
                                           path_name);
-                        if self.emit_errors {
-                            err.help(&msg);
-                        } else {
-                            err.span_help(expr.span, &msg);
-                        }
+                        err.help(&msg);
                         err.emit();
                         self.record_def(expr.id, err_path_resolution());
                     } else {
@@ -2943,9 +2696,13 @@ impl<'a> Resolver<'a> {
                 } else {
                     // Be helpful if the name refers to a struct
                     let path_name = path_names_to_string(path, 0);
-                    let type_res = self.with_no_errors(|this| {
-                        this.resolve_path(expr.id, path, 0, TypeNS)
-                    });
+                    let ast::Path { ref segments, global, .. } = *path;
+                    let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect();
+                    let scope = if global { PathScope::Global } else { PathScope::Lexical };
+                    let type_res = match self.resolve_path(&path, scope, Some(TypeNS), None) {
+                        PathResult::NonModule(type_res) => Some(type_res),
+                        _ => None,
+                    };
 
                     self.record_def(expr.id, err_path_resolution());
 
@@ -2957,16 +2714,11 @@ impl<'a> Resolver<'a> {
                         let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?",
                                           path_name);
 
-                        if self.emit_errors {
-                            err.help(&msg);
-                        } else {
-                            err.span_help(expr.span, &msg);
-                        }
+                        err.help(&msg);
                         err.emit();
                     } else {
                         // Keep reporting some errors even if they're ignored above.
-                        let result = self.resolve_path(expr.id, path, 0, ValueNS);
-                        if result.map(|resolution| resolution.base_def) != Some(Def::Err) {
+                        {
                             let mut method_scope = false;
                             let mut is_static = false;
                             self.ribs[ValueNS].iter().rev().all(|rib| {
@@ -2986,7 +2738,7 @@ impl<'a> Resolver<'a> {
                                               expr.span,
                                               ResolutionError::SelfNotAvailableInStaticMethod);
                             } else {
-                                let last_name = path.segments.last().unwrap().identifier.name;
+                                let last_name = path.last().unwrap().name;
                                 let (mut msg, is_field) =
                                     match self.find_fallback_in_self_type(last_name) {
                                     NoSuggestion => {
@@ -3018,16 +2770,9 @@ impl<'a> Resolver<'a> {
                                     msg = format!("did you mean {}?", msg);
                                 } else {
                                     // we display a help message if this is a module
-                                    let name_path: Vec<_> =
-                                        path.segments.iter().map(|seg| seg.identifier).collect();
-
-                                    match self.resolve_module_path(&name_path[..],
-                                                                   UseLexicalScope,
-                                                                   Some(expr.span)) {
-                                        Success(e) => {
-                                            if let Some(def_type) = e.def() {
-                                                def = def_type;
-                                            }
+                                    match self.resolve_path(&path, scope, None, None) {
+                                        PathResult::Module(module) => {
+                                            def = module.def().unwrap();
                                             context = UnresolvedNameContext::PathIsMod(parent);
                                         },
                                         _ => {},
@@ -3270,7 +3015,7 @@ impl<'a> Resolver<'a> {
                         segms.push(segment);
                         let path = Path {
                             span: span,
-                            global: true,
+                            global: false,
                             segments: segms,
                         };
                         // the entity is accessible in the following cases:
@@ -3320,34 +3065,32 @@ impl<'a> Resolver<'a> {
     }
 
     fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility {
-        let (path, id) = match *vis {
+        let (segments, span, id) = match *vis {
             ast::Visibility::Public => return ty::Visibility::Public,
             ast::Visibility::Crate(_) => return ty::Visibility::Restricted(ast::CRATE_NODE_ID),
-            ast::Visibility::Restricted { ref path, id } => (path, id),
+            ast::Visibility::Restricted { ref path, id } => (&path.segments, path.span, id),
             ast::Visibility::Inherited => {
                 return ty::Visibility::Restricted(self.current_module.normal_ancestor_id.unwrap());
             }
         };
 
-        let segments: Vec<_> = path.segments.iter().map(|seg| seg.identifier).collect();
+        let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect();
         let mut path_resolution = err_path_resolution();
-        let vis = match self.resolve_module_path(&segments, DontUseLexicalScope, Some(path.span)) {
-            Success(module) => {
+        let vis = match self.resolve_path(&path, PathScope::Import, None, Some(span)) {
+            PathResult::Module(module) => {
                 path_resolution = PathResolution::new(module.def().unwrap());
                 ty::Visibility::Restricted(module.normal_ancestor_id.unwrap())
             }
-            Indeterminate => unreachable!(),
-            Failed(err) => {
-                if let Some((span, msg)) = err {
-                    self.session.span_err(span, &format!("failed to resolve module path. {}", msg));
-                }
+            PathResult::Failed(msg, _) => {
+                self.session.span_err(span, &format!("failed to resolve module path. {}", msg));
                 ty::Visibility::Public
             }
+            _ => ty::Visibility::Public,
         };
         self.def_map.insert(id, path_resolution);
         if !self.is_accessible(vis) {
             let msg = format!("visibilities can only be restricted to ancestor modules");
-            self.session.span_err(path.span, &msg);
+            self.session.span_err(span, &msg);
         }
         vis
     }
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index 5c9e8bb9371..c30154b9bba 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -12,11 +12,10 @@ use self::ImportDirectiveSubclass::*;
 
 use {Module, PerNS};
 use Namespace::{self, TypeNS, MacroNS};
-use {NameBinding, NameBindingKind, PrivacyError, ToNameBinding};
+use {NameBinding, NameBindingKind, PathResult, PathScope, PrivacyError, ToNameBinding};
 use ResolveResult;
 use ResolveResult::*;
 use Resolver;
-use UseLexicalScopeFlag::DontUseLexicalScope;
 use {names_to_string, module_to_string};
 use {resolve_error, ResolutionError};
 
@@ -27,6 +26,7 @@ use rustc::hir::def::*;
 use syntax::ast::{Ident, NodeId, Name};
 use syntax::ext::base::Determinacy::{self, Determined, Undetermined};
 use syntax::ext::hygiene::Mark;
+use syntax::symbol::keywords;
 use syntax::util::lev_distance::find_best_match_for_name;
 use syntax_pos::Span;
 
@@ -482,14 +482,13 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
             // For better failure detection, pretend that the import will not define any names
             // while resolving its module path.
             directive.vis.set(ty::Visibility::PrivateExternal);
-            let result =
-                self.resolve_module_path(&directive.module_path, DontUseLexicalScope, None);
+            let result = self.resolve_path(&directive.module_path, PathScope::Import, None, None);
             directive.vis.set(vis);
 
             match result {
-                Success(module) => module,
-                Indeterminate => return Indeterminate,
-                Failed(err) => return Failed(err),
+                PathResult::Module(module) => module,
+                PathResult::Indeterminate => return Indeterminate,
+                _ => return Failed(None),
             }
         };
 
@@ -551,21 +550,20 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
         self.current_module = directive.parent;
 
         let ImportDirective { ref module_path, span, .. } = *directive;
-        let module_result = self.resolve_module_path(&module_path, DontUseLexicalScope, Some(span));
+        let module_result = self.resolve_path(&module_path, PathScope::Import, None, Some(span));
         let module = match module_result {
-            Success(module) => module,
-            Indeterminate => return Indeterminate,
-            Failed(err) => {
-                let self_module = self.module_map[&self.current_module.normal_ancestor_id.unwrap()];
-
-                let resolve_from_self_result = self.resolve_module_path_from_root(
-                    &self_module, &module_path, 0, Some(span));
-
-                return if let Success(_) = resolve_from_self_result {
+            PathResult::Module(module) => module,
+            PathResult::NonModule(..) => return Success(()),
+            PathResult::Indeterminate => return Indeterminate,
+            PathResult::Failed(msg, _) => {
+                let mut path = vec![keywords::SelfValue.ident()];
+                path.extend(module_path);
+                let result = self.resolve_path(&path, PathScope::Import, None, None);
+                return if let PathResult::Module(..) = result {
                     let msg = format!("Did you mean `self::{}`?", &names_to_string(module_path));
                     Failed(Some((span, msg)))
                 } else {
-                    Failed(err)
+                    Failed(Some((span, msg)))
                 };
             },
         };
diff --git a/src/test/compile-fail/issue-28388-1.rs b/src/test/compile-fail/issue-28388-1.rs
index ef97b400b00..bee05cd5313 100644
--- a/src/test/compile-fail/issue-28388-1.rs
+++ b/src/test/compile-fail/issue-28388-1.rs
@@ -10,6 +10,6 @@
 
 // Prefix in imports with empty braces should be resolved and checked privacy, stability, etc.
 
-use foo::{}; //~ ERROR failed to resolve. foo
+use foo::{}; //~ ERROR failed to resolve. Maybe a missing `extern crate foo;`?
 
 fn main() {}