]> git.lizzy.rs Git - rust.git/commitdiff
Extract path resolution submodule
authorAleksey Kladov <aleksey.kladov@gmail.com>
Fri, 8 Nov 2019 21:17:17 +0000 (00:17 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Fri, 8 Nov 2019 21:17:17 +0000 (00:17 +0300)
crates/ra_hir_def/src/nameres.rs
crates/ra_hir_def/src/nameres/collector.rs
crates/ra_hir_def/src/nameres/path_resolution.rs [new file with mode: 0644]

index fb3ba53059abc9c8d6c08a9cd5dfd223bd8e47d3..115b0264cc9b254915ce7b80e23007c66dcd599c 100644 (file)
@@ -52,6 +52,7 @@
 pub mod per_ns;
 mod collector;
 mod mod_resolution;
+mod path_resolution;
 
 #[cfg(test)]
 mod tests;
 use ra_prof::profile;
 use ra_syntax::ast;
 use rustc_hash::{FxHashMap, FxHashSet};
-use test_utils::tested_by;
 
 use crate::{
     builtin_type::BuiltinType,
     db::DefDatabase2,
-    nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId},
-    path::{Path, PathKind},
-    AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId,
+    nameres::{
+        diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
+    },
+    path::Path,
+    AstId, CrateModuleId, ModuleDefId, ModuleId, TraitId,
 };
 
 /// Contains all top-level defs from a macro-expanded crate
@@ -195,39 +197,6 @@ pub struct Resolution {
     pub import: Option<ImportId>,
 }
 
-#[derive(Debug, Clone)]
-struct ResolvePathResult {
-    resolved_def: PerNs,
-    segment_index: Option<usize>,
-    reached_fixedpoint: ReachedFixedPoint,
-}
-
-impl ResolvePathResult {
-    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
-        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
-    }
-
-    fn with(
-        resolved_def: PerNs,
-        reached_fixedpoint: ReachedFixedPoint,
-        segment_index: Option<usize>,
-    ) -> ResolvePathResult {
-        ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum ResolveMode {
-    Import,
-    Other,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum ReachedFixedPoint {
-    Yes,
-    No,
-}
-
 impl CrateDefMap {
     pub(crate) fn crate_def_map_query(
         // Note that this doesn't have `+ AstDatabase`!
@@ -290,210 +259,6 @@ pub fn resolve_path(
         let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
         (res.resolved_def, res.segment_index)
     }
-
-    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
-    // the result.
-    fn resolve_path_fp_with_macro(
-        &self,
-        db: &impl DefDatabase2,
-        mode: ResolveMode,
-        original_module: CrateModuleId,
-        path: &Path,
-    ) -> ResolvePathResult {
-        let mut segments = path.segments.iter().enumerate();
-        let mut curr_per_ns: PerNs = match path.kind {
-            PathKind::DollarCrate(krate) => {
-                if krate == self.krate {
-                    tested_by!(macro_dollar_crate_self);
-                    PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
-                } else {
-                    let def_map = db.crate_def_map(krate);
-                    let module = ModuleId { krate, module_id: def_map.root };
-                    tested_by!(macro_dollar_crate_other);
-                    PerNs::types(module.into())
-                }
-            }
-            PathKind::Crate => {
-                PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
-            }
-            PathKind::Self_ => {
-                PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
-            }
-            // plain import or absolute path in 2015: crate-relative with
-            // fallback to extern prelude (with the simplification in
-            // rust-lang/rust#57745)
-            // FIXME there must be a nicer way to write this condition
-            PathKind::Plain | PathKind::Abs
-                if self.edition == Edition::Edition2015
-                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
-            {
-                let segment = match segments.next() {
-                    Some((_, segment)) => segment,
-                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
-                };
-                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
-                self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
-            }
-            PathKind::Plain => {
-                let segment = match segments.next() {
-                    Some((_, segment)) => segment,
-                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
-                };
-                log::debug!("resolving {:?} in module", segment);
-                self.resolve_name_in_module(db, original_module, &segment.name)
-            }
-            PathKind::Super => {
-                if let Some(p) = self.modules[original_module].parent {
-                    PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
-                } else {
-                    log::debug!("super path in root module");
-                    return ResolvePathResult::empty(ReachedFixedPoint::Yes);
-                }
-            }
-            PathKind::Abs => {
-                // 2018-style absolute path -- only extern prelude
-                let segment = match segments.next() {
-                    Some((_, segment)) => segment,
-                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
-                };
-                if let Some(def) = self.extern_prelude.get(&segment.name) {
-                    log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
-                    PerNs::types(*def)
-                } else {
-                    return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
-                }
-            }
-            PathKind::Type(_) => {
-                // This is handled in `infer::infer_path_expr`
-                // The result returned here does not matter
-                return ResolvePathResult::empty(ReachedFixedPoint::Yes);
-            }
-        };
-
-        for (i, segment) in segments {
-            let curr = match curr_per_ns.take_types() {
-                Some(r) => r,
-                None => {
-                    // we still have path segments left, but the path so far
-                    // didn't resolve in the types namespace => no resolution
-                    // (don't break here because `curr_per_ns` might contain
-                    // something in the value namespace, and it would be wrong
-                    // to return that)
-                    return ResolvePathResult::empty(ReachedFixedPoint::No);
-                }
-            };
-            // resolve segment in curr
-
-            curr_per_ns = match curr {
-                ModuleDefId::ModuleId(module) => {
-                    if module.krate != self.krate {
-                        let path =
-                            Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
-                        log::debug!("resolving {:?} in other crate", path);
-                        let defp_map = db.crate_def_map(module.krate);
-                        let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
-                        return ResolvePathResult::with(
-                            def,
-                            ReachedFixedPoint::Yes,
-                            s.map(|s| s + i),
-                        );
-                    }
-
-                    // Since it is a qualified path here, it should not contains legacy macros
-                    match self[module.module_id].scope.get(&segment.name) {
-                        Some(res) => res.def,
-                        _ => {
-                            log::debug!("path segment {:?} not found", segment.name);
-                            return ResolvePathResult::empty(ReachedFixedPoint::No);
-                        }
-                    }
-                }
-                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
-                    // enum variant
-                    tested_by!(can_import_enum_variant);
-                    let enum_data = db.enum_data(e);
-                    match enum_data.variant(&segment.name) {
-                        Some(local_id) => {
-                            let variant = EnumVariantId { parent: e, local_id };
-                            PerNs::both(variant.into(), variant.into())
-                        }
-                        None => {
-                            return ResolvePathResult::with(
-                                PerNs::types(e.into()),
-                                ReachedFixedPoint::Yes,
-                                Some(i),
-                            );
-                        }
-                    }
-                }
-                s => {
-                    // could be an inherent method call in UFCS form
-                    // (`Struct::method`), or some other kind of associated item
-                    log::debug!(
-                        "path segment {:?} resolved to non-module {:?}, but is not last",
-                        segment.name,
-                        curr,
-                    );
-
-                    return ResolvePathResult::with(
-                        PerNs::types(s),
-                        ReachedFixedPoint::Yes,
-                        Some(i),
-                    );
-                }
-            };
-        }
-        ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
-    }
-
-    fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
-        let from_crate_root =
-            self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
-        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
-
-        from_crate_root.or(from_extern_prelude)
-    }
-
-    fn resolve_name_in_module(
-        &self,
-        db: &impl DefDatabase2,
-        module: CrateModuleId,
-        name: &Name,
-    ) -> PerNs {
-        // Resolve in:
-        //  - legacy scope of macro
-        //  - current module / scope
-        //  - extern prelude
-        //  - std prelude
-        let from_legacy_macro =
-            self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
-        let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
-        let from_extern_prelude =
-            self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
-        let from_prelude = self.resolve_in_prelude(db, name);
-
-        from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
-    }
-
-    fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
-        self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
-    }
-
-    fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
-        if let Some(prelude) = self.prelude {
-            let keep;
-            let def_map = if prelude.krate == self.krate {
-                self
-            } else {
-                // Extend lifetime
-                keep = db.crate_def_map(prelude.krate);
-                &keep
-            };
-            def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
-        } else {
-            PerNs::none()
-        }
-    }
 }
 
 mod diagnostics {
index 9ab378d54da15ffc2cb5f3822f250282407f59d3..aacd50df8862cd9eb2c480b06e43768e4b32400d 100644 (file)
@@ -14,8 +14,8 @@
     attr::Attr,
     db::DefDatabase2,
     nameres::{
-        diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap,
-        ModuleData, ReachedFixedPoint, Resolution, ResolveMode,
+        diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
+        per_ns::PerNs, raw, CrateDefMap, ModuleData, Resolution, ResolveMode,
     },
     path::{Path, PathKind},
     AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId,
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs
new file mode 100644 (file)
index 0000000..95692f8
--- /dev/null
@@ -0,0 +1,261 @@
+//! This modules implements a function to resolve a path `foo::bar::baz` to a
+//! def, which is used within the name resolution.
+//!
+//! When name resolution is finished, the result of resolving a path is either
+//! `Some(def)` or `None`. However, when we are in process of resolving imports
+//! or macros, there's a third possibility:
+//!
+//!   I can't resolve this path right now, but I might be resolve this path
+//!   later, when more macros are expanded.
+//!
+//! `ReachedFixedPoint` signals about this.
+
+use hir_expand::name::Name;
+use ra_db::Edition;
+use test_utils::tested_by;
+
+use crate::{
+    db::DefDatabase2,
+    nameres::{per_ns::PerNs, CrateDefMap},
+    path::{Path, PathKind},
+    AdtId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId,
+};
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) enum ResolveMode {
+    Import,
+    Other,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) enum ReachedFixedPoint {
+    Yes,
+    No,
+}
+
+#[derive(Debug, Clone)]
+pub(super) struct ResolvePathResult {
+    pub(super) resolved_def: PerNs,
+    pub(super) segment_index: Option<usize>,
+    pub(super) reached_fixedpoint: ReachedFixedPoint,
+}
+
+impl ResolvePathResult {
+    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
+        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
+    }
+
+    fn with(
+        resolved_def: PerNs,
+        reached_fixedpoint: ReachedFixedPoint,
+        segment_index: Option<usize>,
+    ) -> ResolvePathResult {
+        ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
+    }
+}
+
+impl CrateDefMap {
+    pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
+        self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
+    }
+
+    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
+    // the result.
+    pub(super) fn resolve_path_fp_with_macro(
+        &self,
+        db: &impl DefDatabase2,
+        mode: ResolveMode,
+        original_module: CrateModuleId,
+        path: &Path,
+    ) -> ResolvePathResult {
+        let mut segments = path.segments.iter().enumerate();
+        let mut curr_per_ns: PerNs = match path.kind {
+            PathKind::DollarCrate(krate) => {
+                if krate == self.krate {
+                    tested_by!(macro_dollar_crate_self);
+                    PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
+                } else {
+                    let def_map = db.crate_def_map(krate);
+                    let module = ModuleId { krate, module_id: def_map.root };
+                    tested_by!(macro_dollar_crate_other);
+                    PerNs::types(module.into())
+                }
+            }
+            PathKind::Crate => {
+                PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
+            }
+            PathKind::Self_ => {
+                PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
+            }
+            // plain import or absolute path in 2015: crate-relative with
+            // fallback to extern prelude (with the simplification in
+            // rust-lang/rust#57745)
+            // FIXME there must be a nicer way to write this condition
+            PathKind::Plain | PathKind::Abs
+                if self.edition == Edition::Edition2015
+                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
+            {
+                let segment = match segments.next() {
+                    Some((_, segment)) => segment,
+                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
+                };
+                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
+                self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
+            }
+            PathKind::Plain => {
+                let segment = match segments.next() {
+                    Some((_, segment)) => segment,
+                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
+                };
+                log::debug!("resolving {:?} in module", segment);
+                self.resolve_name_in_module(db, original_module, &segment.name)
+            }
+            PathKind::Super => {
+                if let Some(p) = self.modules[original_module].parent {
+                    PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
+                } else {
+                    log::debug!("super path in root module");
+                    return ResolvePathResult::empty(ReachedFixedPoint::Yes);
+                }
+            }
+            PathKind::Abs => {
+                // 2018-style absolute path -- only extern prelude
+                let segment = match segments.next() {
+                    Some((_, segment)) => segment,
+                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
+                };
+                if let Some(def) = self.extern_prelude.get(&segment.name) {
+                    log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
+                    PerNs::types(*def)
+                } else {
+                    return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
+                }
+            }
+            PathKind::Type(_) => {
+                // This is handled in `infer::infer_path_expr`
+                // The result returned here does not matter
+                return ResolvePathResult::empty(ReachedFixedPoint::Yes);
+            }
+        };
+
+        for (i, segment) in segments {
+            let curr = match curr_per_ns.take_types() {
+                Some(r) => r,
+                None => {
+                    // we still have path segments left, but the path so far
+                    // didn't resolve in the types namespace => no resolution
+                    // (don't break here because `curr_per_ns` might contain
+                    // something in the value namespace, and it would be wrong
+                    // to return that)
+                    return ResolvePathResult::empty(ReachedFixedPoint::No);
+                }
+            };
+            // resolve segment in curr
+
+            curr_per_ns = match curr {
+                ModuleDefId::ModuleId(module) => {
+                    if module.krate != self.krate {
+                        let path =
+                            Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
+                        log::debug!("resolving {:?} in other crate", path);
+                        let defp_map = db.crate_def_map(module.krate);
+                        let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
+                        return ResolvePathResult::with(
+                            def,
+                            ReachedFixedPoint::Yes,
+                            s.map(|s| s + i),
+                        );
+                    }
+
+                    // Since it is a qualified path here, it should not contains legacy macros
+                    match self[module.module_id].scope.get(&segment.name) {
+                        Some(res) => res.def,
+                        _ => {
+                            log::debug!("path segment {:?} not found", segment.name);
+                            return ResolvePathResult::empty(ReachedFixedPoint::No);
+                        }
+                    }
+                }
+                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
+                    // enum variant
+                    tested_by!(can_import_enum_variant);
+                    let enum_data = db.enum_data(e);
+                    match enum_data.variant(&segment.name) {
+                        Some(local_id) => {
+                            let variant = EnumVariantId { parent: e, local_id };
+                            PerNs::both(variant.into(), variant.into())
+                        }
+                        None => {
+                            return ResolvePathResult::with(
+                                PerNs::types(e.into()),
+                                ReachedFixedPoint::Yes,
+                                Some(i),
+                            );
+                        }
+                    }
+                }
+                s => {
+                    // could be an inherent method call in UFCS form
+                    // (`Struct::method`), or some other kind of associated item
+                    log::debug!(
+                        "path segment {:?} resolved to non-module {:?}, but is not last",
+                        segment.name,
+                        curr,
+                    );
+
+                    return ResolvePathResult::with(
+                        PerNs::types(s),
+                        ReachedFixedPoint::Yes,
+                        Some(i),
+                    );
+                }
+            };
+        }
+        ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
+    }
+
+    fn resolve_name_in_module(
+        &self,
+        db: &impl DefDatabase2,
+        module: CrateModuleId,
+        name: &Name,
+    ) -> PerNs {
+        // Resolve in:
+        //  - legacy scope of macro
+        //  - current module / scope
+        //  - extern prelude
+        //  - std prelude
+        let from_legacy_macro =
+            self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
+        let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
+        let from_extern_prelude =
+            self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
+        let from_prelude = self.resolve_in_prelude(db, name);
+
+        from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
+    }
+
+    fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
+        let from_crate_root =
+            self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
+        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
+
+        from_crate_root.or(from_extern_prelude)
+    }
+
+    fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
+        if let Some(prelude) = self.prelude {
+            let keep;
+            let def_map = if prelude.krate == self.krate {
+                self
+            } else {
+                // Extend lifetime
+                keep = db.crate_def_map(prelude.krate);
+                &keep
+            };
+            def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
+        } else {
+            PerNs::none()
+        }
+    }
+}