diff options
Diffstat (limited to 'crates/ra_hir/src/nameres.rs')
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 462 |
1 files changed, 362 insertions, 100 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 2248842de..30e959c04 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -1,35 +1,128 @@ | |||
1 | //! Name resolution algorithm. The end result of the algorithm is an `ItemMap`: | 1 | /// This module implements import-resolution/macro expansion algorithm. |
2 | //! a map which maps each module to its scope: the set of items visible in the | 2 | /// |
3 | //! module. That is, we only resolve imports here, name resolution of item | 3 | /// The result of this module is `CrateDefMap`: a datastructure which contains: |
4 | //! bodies will be done in a separate step. | 4 | /// |
5 | //! | 5 | /// * a tree of modules for the crate |
6 | //! Like Rustc, we use an interactive per-crate algorithm: we start with scopes | 6 | /// * for each module, a set of items visible in the module (directly declared |
7 | //! containing only directly defined items, and then iteratively resolve | 7 | /// or imported) |
8 | //! imports. | 8 | /// |
9 | //! | 9 | /// Note that `CrateDefMap` contains fully macro expanded code. |
10 | //! To make this work nicely in the IDE scenario, we place `InputModuleItems` | 10 | /// |
11 | //! in between raw syntax and name resolution. `InputModuleItems` are computed | 11 | /// Computing `CrateDefMap` can be partitioned into several logically |
12 | //! using only the module's syntax, and it is all directly defined items plus | 12 | /// independent "phases". The phases are mutually recursive though, there's no |
13 | //! imports. The plan is to make `InputModuleItems` independent of local | 13 | /// stric ordering. |
14 | //! modifications (that is, typing inside a function should not change IMIs), | 14 | /// |
15 | //! so that the results of name resolution can be preserved unless the module | 15 | /// ## Collecting RawItems |
16 | //! structure itself is modified. | 16 | /// |
17 | pub(crate) mod lower; | 17 | /// This happens in the `raw` module, which parses a single source file into a |
18 | pub(crate) mod crate_def_map; | 18 | /// set of top-level items. Nested importa are desugared to flat imports in |
19 | /// this phase. Macro calls are represented as a triple of (Path, Option<Name>, | ||
20 | /// TokenTree). | ||
21 | /// | ||
22 | /// ## Collecting Modules | ||
23 | /// | ||
24 | /// This happens in the `collector` module. In this phase, we recursively walk | ||
25 | /// tree of modules, collect raw items from submodules, populate module scopes | ||
26 | /// with defined items (so, we assign item ids in this phase) and record the set | ||
27 | /// of unresovled imports and macros. | ||
28 | /// | ||
29 | /// While we walk tree of modules, we also record macro_rules defenitions and | ||
30 | /// expand calls to macro_rules defined macros. | ||
31 | /// | ||
32 | /// ## Resolving Imports | ||
33 | /// | ||
34 | /// TBD | ||
35 | /// | ||
36 | /// ## Resolving Macros | ||
37 | /// | ||
38 | /// While macro_rules from the same crate use a global mutable namespace, macros | ||
39 | /// from other crates (including proc-macros) can be used with `foo::bar!` | ||
40 | /// syntax. | ||
41 | /// | ||
42 | /// TBD; | ||
43 | |||
44 | mod per_ns; | ||
45 | mod raw; | ||
46 | mod collector; | ||
47 | #[cfg(test)] | ||
48 | mod tests; | ||
49 | |||
50 | use std::sync::Arc; | ||
19 | 51 | ||
20 | use rustc_hash::FxHashMap; | 52 | use rustc_hash::FxHashMap; |
21 | use ra_db::Edition; | 53 | use ra_arena::{Arena, RawId, impl_arena_id}; |
54 | use ra_db::{FileId, Edition}; | ||
55 | use test_utils::tested_by; | ||
22 | 56 | ||
23 | use crate::{ | 57 | use crate::{ |
24 | ModuleDef, Name, | 58 | ModuleDef, Name, Crate, Module, Problem, |
25 | nameres::lower::ImportId, | 59 | PersistentHirDatabase, Path, PathKind, HirFileId, |
60 | ids::{SourceItemId, SourceFileItemId}, | ||
26 | }; | 61 | }; |
27 | 62 | ||
28 | pub(crate) use self::crate_def_map::{CrateDefMap, ModuleId}; | 63 | pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap}; |
64 | |||
65 | pub use self::per_ns::{PerNs, Namespace}; | ||
66 | |||
67 | /// Contans all top-level defs from a macro-expanded crate | ||
68 | #[derive(Debug, PartialEq, Eq)] | ||
69 | pub struct CrateDefMap { | ||
70 | krate: Crate, | ||
71 | edition: Edition, | ||
72 | /// The prelude module for this crate. This either comes from an import | ||
73 | /// marked with the `prelude_import` attribute, or (in the normal case) from | ||
74 | /// a dependency (`std` or `core`). | ||
75 | prelude: Option<Module>, | ||
76 | extern_prelude: FxHashMap<Name, ModuleDef>, | ||
77 | root: ModuleId, | ||
78 | modules: Arena<ModuleId, ModuleData>, | ||
79 | public_macros: FxHashMap<Name, mbe::MacroRules>, | ||
80 | problems: CrateDefMapProblems, | ||
81 | } | ||
82 | |||
83 | impl std::ops::Index<ModuleId> for CrateDefMap { | ||
84 | type Output = ModuleData; | ||
85 | fn index(&self, id: ModuleId) -> &ModuleData { | ||
86 | &self.modules[id] | ||
87 | } | ||
88 | } | ||
89 | |||
90 | /// An ID of a module, **local** to a specific crate | ||
91 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
92 | pub(crate) struct ModuleId(RawId); | ||
93 | impl_arena_id!(ModuleId); | ||
94 | |||
95 | #[derive(Default, Debug, PartialEq, Eq)] | ||
96 | pub(crate) struct ModuleData { | ||
97 | pub(crate) parent: Option<ModuleId>, | ||
98 | pub(crate) children: FxHashMap<Name, ModuleId>, | ||
99 | pub(crate) scope: ModuleScope, | ||
100 | /// None for root | ||
101 | pub(crate) declaration: Option<SourceItemId>, | ||
102 | /// None for inline modules. | ||
103 | /// | ||
104 | /// Note that non-inline modules, by definition, live inside non-macro file. | ||
105 | pub(crate) definition: Option<FileId>, | ||
106 | } | ||
107 | |||
108 | #[derive(Default, Debug, PartialEq, Eq)] | ||
109 | pub(crate) struct CrateDefMapProblems { | ||
110 | problems: Vec<(SourceItemId, Problem)>, | ||
111 | } | ||
112 | |||
113 | impl CrateDefMapProblems { | ||
114 | fn add(&mut self, source_item_id: SourceItemId, problem: Problem) { | ||
115 | self.problems.push((source_item_id, problem)) | ||
116 | } | ||
117 | |||
118 | pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a { | ||
119 | self.problems.iter().map(|(s, p)| (s, p)) | ||
120 | } | ||
121 | } | ||
29 | 122 | ||
30 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | 123 | #[derive(Debug, Default, PartialEq, Eq, Clone)] |
31 | pub struct ModuleScope { | 124 | pub struct ModuleScope { |
32 | pub(crate) items: FxHashMap<Name, Resolution>, | 125 | items: FxHashMap<Name, Resolution>, |
33 | } | 126 | } |
34 | 127 | ||
35 | impl ModuleScope { | 128 | impl ModuleScope { |
@@ -41,8 +134,6 @@ impl ModuleScope { | |||
41 | } | 134 | } |
42 | } | 135 | } |
43 | 136 | ||
44 | /// `Resolution` is basically `DefId` atm, but it should account for stuff like | ||
45 | /// multiple namespaces, ambiguity and errors. | ||
46 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | 137 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
47 | pub struct Resolution { | 138 | pub struct Resolution { |
48 | /// None for unresolved | 139 | /// None for unresolved |
@@ -51,114 +142,285 @@ pub struct Resolution { | |||
51 | pub import: Option<ImportId>, | 142 | pub import: Option<ImportId>, |
52 | } | 143 | } |
53 | 144 | ||
54 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | 145 | #[derive(Debug, Clone)] |
55 | pub enum Namespace { | 146 | struct ResolvePathResult { |
56 | Types, | 147 | resolved_def: PerNs<ModuleDef>, |
57 | Values, | 148 | segment_index: Option<usize>, |
149 | reached_fixedpoint: ReachedFixedPoint, | ||
58 | } | 150 | } |
59 | 151 | ||
60 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 152 | impl ResolvePathResult { |
61 | pub struct PerNs<T> { | 153 | fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { |
62 | pub types: Option<T>, | 154 | ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None) |
63 | pub values: Option<T>, | 155 | } |
64 | } | ||
65 | 156 | ||
66 | impl<T> Default for PerNs<T> { | 157 | fn with( |
67 | fn default() -> Self { | 158 | resolved_def: PerNs<ModuleDef>, |
68 | PerNs { types: None, values: None } | 159 | reached_fixedpoint: ReachedFixedPoint, |
160 | segment_index: Option<usize>, | ||
161 | ) -> ResolvePathResult { | ||
162 | ResolvePathResult { resolved_def, reached_fixedpoint, segment_index } | ||
69 | } | 163 | } |
70 | } | 164 | } |
71 | 165 | ||
72 | impl<T> PerNs<T> { | 166 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
73 | pub fn none() -> PerNs<T> { | 167 | enum ResolveMode { |
74 | PerNs { types: None, values: None } | 168 | Import, |
75 | } | 169 | Other, |
170 | } | ||
76 | 171 | ||
77 | pub fn values(t: T) -> PerNs<T> { | 172 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
78 | PerNs { types: None, values: Some(t) } | 173 | enum ReachedFixedPoint { |
79 | } | 174 | Yes, |
175 | No, | ||
176 | } | ||
80 | 177 | ||
81 | pub fn types(t: T) -> PerNs<T> { | 178 | impl CrateDefMap { |
82 | PerNs { types: Some(t), values: None } | 179 | pub(crate) fn crate_def_map_query( |
180 | db: &impl PersistentHirDatabase, | ||
181 | krate: Crate, | ||
182 | ) -> Arc<CrateDefMap> { | ||
183 | let def_map = { | ||
184 | let edition = krate.edition(db); | ||
185 | let mut modules: Arena<ModuleId, ModuleData> = Arena::default(); | ||
186 | let root = modules.alloc(ModuleData::default()); | ||
187 | CrateDefMap { | ||
188 | krate, | ||
189 | edition, | ||
190 | extern_prelude: FxHashMap::default(), | ||
191 | prelude: None, | ||
192 | root, | ||
193 | modules, | ||
194 | public_macros: FxHashMap::default(), | ||
195 | problems: CrateDefMapProblems::default(), | ||
196 | } | ||
197 | }; | ||
198 | let def_map = collector::collect_defs(db, def_map); | ||
199 | Arc::new(def_map) | ||
83 | } | 200 | } |
84 | 201 | ||
85 | pub fn both(types: T, values: T) -> PerNs<T> { | 202 | pub(crate) fn root(&self) -> ModuleId { |
86 | PerNs { types: Some(types), values: Some(values) } | 203 | self.root |
87 | } | 204 | } |
88 | 205 | ||
89 | pub fn is_none(&self) -> bool { | 206 | pub(crate) fn problems(&self) -> &CrateDefMapProblems { |
90 | self.types.is_none() && self.values.is_none() | 207 | &self.problems |
91 | } | 208 | } |
92 | 209 | ||
93 | pub fn is_both(&self) -> bool { | 210 | pub(crate) fn mk_module(&self, module_id: ModuleId) -> Module { |
94 | self.types.is_some() && self.values.is_some() | 211 | Module { krate: self.krate, module_id } |
95 | } | 212 | } |
96 | 213 | ||
97 | pub fn take(self, namespace: Namespace) -> Option<T> { | 214 | pub(crate) fn prelude(&self) -> Option<Module> { |
98 | match namespace { | 215 | self.prelude |
99 | Namespace::Types => self.types, | ||
100 | Namespace::Values => self.values, | ||
101 | } | ||
102 | } | 216 | } |
103 | 217 | ||
104 | pub fn take_types(self) -> Option<T> { | 218 | pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> { |
105 | self.take(Namespace::Types) | 219 | &self.extern_prelude |
106 | } | 220 | } |
107 | 221 | ||
108 | pub fn take_values(self) -> Option<T> { | 222 | pub(crate) fn find_module_by_source( |
109 | self.take(Namespace::Values) | 223 | &self, |
224 | file_id: HirFileId, | ||
225 | decl_id: Option<SourceFileItemId>, | ||
226 | ) -> Option<ModuleId> { | ||
227 | let decl_id = decl_id.map(|it| it.with_file_id(file_id)); | ||
228 | let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { | ||
229 | if decl_id.is_some() { | ||
230 | module_data.declaration == decl_id | ||
231 | } else { | ||
232 | module_data.definition.map(|it| it.into()) == Some(file_id) | ||
233 | } | ||
234 | })?; | ||
235 | Some(module_id) | ||
110 | } | 236 | } |
111 | 237 | ||
112 | pub fn get(&self, namespace: Namespace) -> Option<&T> { | 238 | pub(crate) fn resolve_path( |
113 | self.as_ref().take(namespace) | 239 | &self, |
240 | db: &impl PersistentHirDatabase, | ||
241 | original_module: ModuleId, | ||
242 | path: &Path, | ||
243 | ) -> (PerNs<ModuleDef>, Option<usize>) { | ||
244 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); | ||
245 | (res.resolved_def, res.segment_index) | ||
114 | } | 246 | } |
115 | 247 | ||
116 | pub fn as_ref(&self) -> PerNs<&T> { | 248 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change |
117 | PerNs { types: self.types.as_ref(), values: self.values.as_ref() } | 249 | // the result. |
118 | } | 250 | fn resolve_path_fp( |
251 | &self, | ||
252 | db: &impl PersistentHirDatabase, | ||
253 | mode: ResolveMode, | ||
254 | original_module: ModuleId, | ||
255 | path: &Path, | ||
256 | ) -> ResolvePathResult { | ||
257 | let mut segments = path.segments.iter().enumerate(); | ||
258 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { | ||
259 | PathKind::Crate => { | ||
260 | PerNs::types(Module { krate: self.krate, module_id: self.root }.into()) | ||
261 | } | ||
262 | PathKind::Self_ => { | ||
263 | PerNs::types(Module { krate: self.krate, module_id: original_module }.into()) | ||
264 | } | ||
265 | // plain import or absolute path in 2015: crate-relative with | ||
266 | // fallback to extern prelude (with the simplification in | ||
267 | // rust-lang/rust#57745) | ||
268 | // TODO there must be a nicer way to write this condition | ||
269 | PathKind::Plain | PathKind::Abs | ||
270 | if self.edition == Edition::Edition2015 | ||
271 | && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => | ||
272 | { | ||
273 | let segment = match segments.next() { | ||
274 | Some((_, segment)) => segment, | ||
275 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
276 | }; | ||
277 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | ||
278 | self.resolve_name_in_crate_root_or_extern_prelude(&segment.name) | ||
279 | } | ||
280 | PathKind::Plain => { | ||
281 | let segment = match segments.next() { | ||
282 | Some((_, segment)) => segment, | ||
283 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
284 | }; | ||
285 | log::debug!("resolving {:?} in module", segment); | ||
286 | self.resolve_name_in_module(db, original_module, &segment.name) | ||
287 | } | ||
288 | PathKind::Super => { | ||
289 | if let Some(p) = self.modules[original_module].parent { | ||
290 | PerNs::types(Module { krate: self.krate, module_id: p }.into()) | ||
291 | } else { | ||
292 | log::debug!("super path in root module"); | ||
293 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | ||
294 | } | ||
295 | } | ||
296 | PathKind::Abs => { | ||
297 | // 2018-style absolute path -- only extern prelude | ||
298 | let segment = match segments.next() { | ||
299 | Some((_, segment)) => segment, | ||
300 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | ||
301 | }; | ||
302 | if let Some(def) = self.extern_prelude.get(&segment.name) { | ||
303 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); | ||
304 | PerNs::types(*def) | ||
305 | } else { | ||
306 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude | ||
307 | } | ||
308 | } | ||
309 | }; | ||
119 | 310 | ||
120 | pub fn or(self, other: PerNs<T>) -> PerNs<T> { | 311 | for (i, segment) in segments { |
121 | PerNs { types: self.types.or(other.types), values: self.values.or(other.values) } | 312 | let curr = match curr_per_ns.as_ref().take_types() { |
122 | } | 313 | Some(r) => r, |
314 | None => { | ||
315 | // we still have path segments left, but the path so far | ||
316 | // didn't resolve in the types namespace => no resolution | ||
317 | // (don't break here because `curr_per_ns` might contain | ||
318 | // something in the value namespace, and it would be wrong | ||
319 | // to return that) | ||
320 | return ResolvePathResult::empty(ReachedFixedPoint::No); | ||
321 | } | ||
322 | }; | ||
323 | // resolve segment in curr | ||
123 | 324 | ||
124 | pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> { | 325 | curr_per_ns = match curr { |
125 | PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) } | 326 | ModuleDef::Module(module) => { |
126 | } | 327 | if module.krate != self.krate { |
328 | let path = Path { | ||
329 | segments: path.segments[i..].iter().cloned().collect(), | ||
330 | kind: PathKind::Self_, | ||
331 | }; | ||
332 | log::debug!("resolving {:?} in other crate", path); | ||
333 | let defp_map = db.crate_def_map(module.krate); | ||
334 | let (def, s) = defp_map.resolve_path(db, module.module_id, &path); | ||
335 | return ResolvePathResult::with( | ||
336 | def, | ||
337 | ReachedFixedPoint::Yes, | ||
338 | s.map(|s| s + i), | ||
339 | ); | ||
340 | } | ||
127 | 341 | ||
128 | pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> { | 342 | match self[module.module_id].scope.items.get(&segment.name) { |
129 | PerNs { types: self.types.map(&f), values: self.values.map(&f) } | 343 | Some(res) if !res.def.is_none() => res.def, |
344 | _ => { | ||
345 | log::debug!("path segment {:?} not found", segment.name); | ||
346 | return ResolvePathResult::empty(ReachedFixedPoint::No); | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | ModuleDef::Enum(e) => { | ||
351 | // enum variant | ||
352 | tested_by!(can_import_enum_variant); | ||
353 | match e.variant(db, &segment.name) { | ||
354 | Some(variant) => PerNs::both(variant.into(), variant.into()), | ||
355 | None => { | ||
356 | return ResolvePathResult::with( | ||
357 | PerNs::types((*e).into()), | ||
358 | ReachedFixedPoint::Yes, | ||
359 | Some(i), | ||
360 | ); | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | s => { | ||
365 | // could be an inherent method call in UFCS form | ||
366 | // (`Struct::method`), or some other kind of associated item | ||
367 | log::debug!( | ||
368 | "path segment {:?} resolved to non-module {:?}, but is not last", | ||
369 | segment.name, | ||
370 | curr, | ||
371 | ); | ||
372 | |||
373 | return ResolvePathResult::with( | ||
374 | PerNs::types((*s).into()), | ||
375 | ReachedFixedPoint::Yes, | ||
376 | Some(i), | ||
377 | ); | ||
378 | } | ||
379 | }; | ||
380 | } | ||
381 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) | ||
130 | } | 382 | } |
131 | } | ||
132 | 383 | ||
133 | #[derive(Debug, Clone)] | 384 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { |
134 | struct ResolvePathResult { | 385 | let from_crate_root = |
135 | resolved_def: PerNs<ModuleDef>, | 386 | self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def); |
136 | segment_index: Option<usize>, | 387 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); |
137 | reached_fixedpoint: ReachedFixedPoint, | ||
138 | } | ||
139 | 388 | ||
140 | impl ResolvePathResult { | 389 | from_crate_root.or(from_extern_prelude) |
141 | fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { | ||
142 | ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None) | ||
143 | } | 390 | } |
144 | 391 | ||
145 | fn with( | 392 | pub(crate) fn resolve_name_in_module( |
146 | resolved_def: PerNs<ModuleDef>, | 393 | &self, |
147 | reached_fixedpoint: ReachedFixedPoint, | 394 | db: &impl PersistentHirDatabase, |
148 | segment_index: Option<usize>, | 395 | module: ModuleId, |
149 | ) -> ResolvePathResult { | 396 | name: &Name, |
150 | ResolvePathResult { resolved_def, reached_fixedpoint, segment_index } | 397 | ) -> PerNs<ModuleDef> { |
398 | // Resolve in: | ||
399 | // - current module / scope | ||
400 | // - extern prelude | ||
401 | // - std prelude | ||
402 | let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | ||
403 | let from_extern_prelude = | ||
404 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); | ||
405 | let from_prelude = self.resolve_in_prelude(db, name); | ||
406 | |||
407 | from_scope.or(from_extern_prelude).or(from_prelude) | ||
151 | } | 408 | } |
152 | } | ||
153 | 409 | ||
154 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 410 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { |
155 | enum ResolveMode { | 411 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) |
156 | Import, | 412 | } |
157 | Other, | ||
158 | } | ||
159 | 413 | ||
160 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 414 | fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> { |
161 | enum ReachedFixedPoint { | 415 | if let Some(prelude) = self.prelude { |
162 | Yes, | 416 | let resolution = if prelude.krate == self.krate { |
163 | No, | 417 | self[prelude.module_id].scope.items.get(name).cloned() |
418 | } else { | ||
419 | db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned() | ||
420 | }; | ||
421 | resolution.map(|r| r.def).unwrap_or_else(PerNs::none) | ||
422 | } else { | ||
423 | PerNs::none() | ||
424 | } | ||
425 | } | ||
164 | } | 426 | } |