mun_hir/name_resolution/
path_resolution.rs

1use crate::{
2    ids::{ItemDefinitionId, ModuleId},
3    item_scope::BUILTIN_SCOPE,
4    module_tree::LocalModuleId,
5    package_defs::PackageDefs,
6    DefDatabase, Name, PackageId, Path, PathKind, PerNs, Visibility,
7};
8use std::iter::successors;
9
10/// Indicates whether or not any newly resolved import statements will actually change the outcome
11/// of an operation. This is useful to know if more iterations of an algorithm might be required, or
12/// whether its hopeless.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ReachedFixedPoint {
15    Yes,
16    No,
17}
18
19/// Contains the result of resolving a path. It contains how far the path was able to be resolved
20/// as well as the resolved values or types so far.
21#[derive(Debug, Clone)]
22pub(crate) struct ResolvePathResult {
23    pub(crate) resolved_def: PerNs<(ItemDefinitionId, Visibility)>,
24    pub(crate) segment_index: Option<usize>,
25    pub(crate) reached_fixedpoint: ReachedFixedPoint,
26    pub(crate) package: Option<PackageId>,
27}
28
29impl ResolvePathResult {
30    /// Constructs an empty `ResolvePathResult`
31    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
32        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
33    }
34
35    /// Constructs a new instance of `ResolvePathResult`
36    fn with(
37        resolved_def: PerNs<(ItemDefinitionId, Visibility)>,
38        reached_fixedpoint: ReachedFixedPoint,
39        segment_index: Option<usize>,
40        package: Option<PackageId>,
41    ) -> ResolvePathResult {
42        ResolvePathResult {
43            resolved_def,
44            segment_index,
45            reached_fixedpoint,
46            package,
47        }
48    }
49}
50
51impl PackageDefs {
52    /// Resolves the specified `path` from within the specified `module`. Also optionally returns
53    /// which part of the path was resolved, if this is not `None` it means the path didn't resolve
54    /// completely yet.
55    pub(crate) fn resolve_path_in_module(
56        &self,
57        db: &dyn DefDatabase,
58        module: LocalModuleId,
59        path: &Path,
60    ) -> (PerNs<(ItemDefinitionId, Visibility)>, Option<usize>) {
61        let res = self.resolve_path_with_fixedpoint(db, module, path);
62        (res.resolved_def, res.segment_index)
63    }
64
65    /// Resolves the specified `name` from within the specified `module`
66    fn resolve_name_in_module(
67        &self,
68        _db: &dyn DefDatabase,
69        module: LocalModuleId,
70        name: &Name,
71    ) -> PerNs<(ItemDefinitionId, Visibility)> {
72        self[module]
73            .get(name)
74            .or(BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none))
75    }
76
77    /// Resolves the specified `path` from within the specified `module`. Also returns whether or
78    /// not additions to the `PackageDef` would change the result or whether a fixed point has been
79    /// reached. This is useful when resolving all imports.
80    pub(crate) fn resolve_path_with_fixedpoint(
81        &self,
82        db: &dyn DefDatabase,
83        original_module: LocalModuleId,
84        path: &Path,
85    ) -> ResolvePathResult {
86        let mut segments = path.segments.iter().enumerate();
87        let mut curr_per_ns: PerNs<(ItemDefinitionId, Visibility)> = match path.kind {
88            PathKind::Plain => {
89                let (_, segment) = match segments.next() {
90                    Some((idx, segment)) => (idx, segment),
91                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
92                };
93                self.resolve_name_in_module(db, original_module, segment)
94            }
95            PathKind::Super(lvl) => {
96                let m = successors(Some(original_module), |m| self.module_tree[*m].parent)
97                    .nth(lvl as usize);
98                if let Some(local_id) = m {
99                    PerNs::types((
100                        ModuleId {
101                            package: self.module_tree.package,
102                            local_id,
103                        }
104                        .into(),
105                        Visibility::Public,
106                    ))
107                } else {
108                    return ResolvePathResult::empty(ReachedFixedPoint::Yes);
109                }
110            }
111            PathKind::Package => PerNs::types((
112                ModuleId {
113                    package: self.module_tree.package,
114                    local_id: self.module_tree.root,
115                }
116                .into(),
117                Visibility::Public,
118            )),
119        };
120
121        for (i, segment) in segments {
122            let (curr, vis) = match curr_per_ns.take_types() {
123                Some(r) => r,
124                None => {
125                    return ResolvePathResult::empty(ReachedFixedPoint::No);
126                }
127            };
128
129            curr_per_ns = match curr {
130                ItemDefinitionId::ModuleId(module) => self[module.local_id].get(segment),
131                // TODO: Enum variants
132                s => {
133                    return ResolvePathResult::with(
134                        PerNs::types((s, vis)),
135                        ReachedFixedPoint::Yes,
136                        Some(i),
137                        Some(self.module_tree.package),
138                    );
139                }
140            };
141        }
142
143        ResolvePathResult::with(
144            curr_per_ns,
145            ReachedFixedPoint::Yes,
146            None,
147            Some(self.module_tree.package),
148        )
149    }
150}