diff options
author | Florian Diebold <[email protected]> | 2020-02-21 14:34:06 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-02-21 14:39:51 +0000 |
commit | 31af774254d1fb4e9105ba050546db16b9b54fb6 (patch) | |
tree | 587c84243cf6f4db8fa9d403f50178afab7ffe4f /crates/ra_hir_def/src | |
parent | e3037c2631ecb55996b676ce2c18b9df1858abaa (diff) |
Refactor how builtins are resolved
This fixes autocompletion suggesting e.g. self::usize.
Diffstat (limited to 'crates/ra_hir_def/src')
-rw-r--r-- | crates/ra_hir_def/src/item_scope.rs | 33 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/path_resolution.rs | 63 | ||||
-rw-r--r-- | crates/ra_hir_def/src/resolver.rs | 13 |
3 files changed, 48 insertions, 61 deletions
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index 6e958ca75..5e943b780 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -30,7 +30,7 @@ pub struct ItemScope { | |||
30 | legacy_macros: FxHashMap<Name, MacroDefId>, | 30 | legacy_macros: FxHashMap<Name, MacroDefId>, |
31 | } | 31 | } |
32 | 32 | ||
33 | static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | 33 | pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { |
34 | BuiltinType::ALL | 34 | BuiltinType::ALL |
35 | .iter() | 35 | .iter() |
36 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) | 36 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) |
@@ -40,9 +40,9 @@ static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | |||
40 | /// Shadow mode for builtin type which can be shadowed by module. | 40 | /// Shadow mode for builtin type which can be shadowed by module. |
41 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 41 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
42 | pub(crate) enum BuiltinShadowMode { | 42 | pub(crate) enum BuiltinShadowMode { |
43 | // Prefer Module | 43 | /// Prefer user-defined modules (or other types) over builtins. |
44 | Module, | 44 | Module, |
45 | // Prefer Other Types | 45 | /// Prefer builtins over user-defined modules (but not other types). |
46 | Other, | 46 | Other, |
47 | } | 47 | } |
48 | 48 | ||
@@ -51,7 +51,7 @@ pub(crate) enum BuiltinShadowMode { | |||
51 | impl ItemScope { | 51 | impl ItemScope { |
52 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | 52 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { |
53 | //FIXME: shadowing | 53 | //FIXME: shadowing |
54 | self.visible.iter().chain(BUILTIN_SCOPE.iter()).map(|(n, def)| (n, *def)) | 54 | self.visible.iter().map(|(n, def)| (n, *def)) |
55 | } | 55 | } |
56 | 56 | ||
57 | pub fn entries_without_primitives<'a>( | 57 | pub fn entries_without_primitives<'a>( |
@@ -79,29 +79,8 @@ impl ItemScope { | |||
79 | } | 79 | } |
80 | 80 | ||
81 | /// Get a name from current module scope, legacy macros are not included | 81 | /// Get a name from current module scope, legacy macros are not included |
82 | pub(crate) fn get(&self, name: &Name, shadow: BuiltinShadowMode) -> PerNs { | 82 | pub(crate) fn get(&self, name: &Name) -> PerNs { |
83 | match shadow { | 83 | self.visible.get(name).copied().unwrap_or_else(PerNs::none) |
84 | BuiltinShadowMode::Module => self | ||
85 | .visible | ||
86 | .get(name) | ||
87 | .or_else(|| BUILTIN_SCOPE.get(name)) | ||
88 | .copied() | ||
89 | .unwrap_or_else(PerNs::none), | ||
90 | BuiltinShadowMode::Other => { | ||
91 | let item = self.visible.get(name).copied(); | ||
92 | if let Some(def) = item { | ||
93 | if let Some(ModuleDefId::ModuleId(_)) = def.take_types() { | ||
94 | return BUILTIN_SCOPE | ||
95 | .get(name) | ||
96 | .copied() | ||
97 | .or(item) | ||
98 | .unwrap_or_else(PerNs::none); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | item.or_else(|| BUILTIN_SCOPE.get(name).copied()).unwrap_or_else(PerNs::none) | ||
103 | } | ||
104 | } | ||
105 | } | 84 | } |
106 | 85 | ||
107 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { | 86 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { |
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs index fd6422d60..c058e70aa 100644 --- a/crates/ra_hir_def/src/nameres/path_resolution.rs +++ b/crates/ra_hir_def/src/nameres/path_resolution.rs | |||
@@ -18,6 +18,7 @@ use test_utils::tested_by; | |||
18 | 18 | ||
19 | use crate::{ | 19 | use crate::{ |
20 | db::DefDatabase, | 20 | db::DefDatabase, |
21 | item_scope::BUILTIN_SCOPE, | ||
21 | nameres::{BuiltinShadowMode, CrateDefMap}, | 22 | nameres::{BuiltinShadowMode, CrateDefMap}, |
22 | path::{ModPath, PathKind}, | 23 | path::{ModPath, PathKind}, |
23 | per_ns::PerNs, | 24 | per_ns::PerNs, |
@@ -103,15 +104,6 @@ impl CrateDefMap { | |||
103 | path: &ModPath, | 104 | path: &ModPath, |
104 | shadow: BuiltinShadowMode, | 105 | shadow: BuiltinShadowMode, |
105 | ) -> ResolvePathResult { | 106 | ) -> ResolvePathResult { |
106 | // if it is not the last segment, we prefer the module to the builtin | ||
107 | let prefer_module = |index| { | ||
108 | if index == path.segments.len() - 1 { | ||
109 | shadow | ||
110 | } else { | ||
111 | BuiltinShadowMode::Module | ||
112 | } | ||
113 | }; | ||
114 | |||
115 | let mut segments = path.segments.iter().enumerate(); | 107 | let mut segments = path.segments.iter().enumerate(); |
116 | let mut curr_per_ns: PerNs = match path.kind { | 108 | let mut curr_per_ns: PerNs = match path.kind { |
117 | PathKind::DollarCrate(krate) => { | 109 | PathKind::DollarCrate(krate) => { |
@@ -140,20 +132,29 @@ impl CrateDefMap { | |||
140 | if self.edition == Edition::Edition2015 | 132 | if self.edition == Edition::Edition2015 |
141 | && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => | 133 | && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => |
142 | { | 134 | { |
143 | let (idx, segment) = match segments.next() { | 135 | let (_, segment) = match segments.next() { |
144 | Some((idx, segment)) => (idx, segment), | 136 | Some((idx, segment)) => (idx, segment), |
145 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 137 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
146 | }; | 138 | }; |
147 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | 139 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); |
148 | self.resolve_name_in_crate_root_or_extern_prelude(&segment, prefer_module(idx)) | 140 | self.resolve_name_in_crate_root_or_extern_prelude(&segment) |
149 | } | 141 | } |
150 | PathKind::Plain => { | 142 | PathKind::Plain => { |
151 | let (idx, segment) = match segments.next() { | 143 | let (_, segment) = match segments.next() { |
152 | Some((idx, segment)) => (idx, segment), | 144 | Some((idx, segment)) => (idx, segment), |
153 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 145 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
154 | }; | 146 | }; |
147 | // The first segment may be a builtin type. If the path has more | ||
148 | // than one segment, we first try resolving it as a module | ||
149 | // anyway. | ||
150 | // FIXME: If the next segment doesn't resolve in the module and | ||
151 | // BuiltinShadowMode wasn't Module, then we need to try | ||
152 | // resolving it as a builtin. | ||
153 | let prefer_module = | ||
154 | if path.segments.len() == 1 { shadow } else { BuiltinShadowMode::Module }; | ||
155 | |||
155 | log::debug!("resolving {:?} in module", segment); | 156 | log::debug!("resolving {:?} in module", segment); |
156 | self.resolve_name_in_module(db, original_module, &segment, prefer_module(idx)) | 157 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) |
157 | } | 158 | } |
158 | PathKind::Super(lvl) => { | 159 | PathKind::Super(lvl) => { |
159 | let m = successors(Some(original_module), |m| self.modules[*m].parent) | 160 | let m = successors(Some(original_module), |m| self.modules[*m].parent) |
@@ -216,7 +217,7 @@ impl CrateDefMap { | |||
216 | } | 217 | } |
217 | 218 | ||
218 | // Since it is a qualified path here, it should not contains legacy macros | 219 | // Since it is a qualified path here, it should not contains legacy macros |
219 | self[module.local_id].scope.get(&segment, prefer_module(i)) | 220 | self[module.local_id].scope.get(&segment) |
220 | } | 221 | } |
221 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { | 222 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { |
222 | // enum variant | 223 | // enum variant |
@@ -275,33 +276,35 @@ impl CrateDefMap { | |||
275 | .scope | 276 | .scope |
276 | .get_legacy_macro(name) | 277 | .get_legacy_macro(name) |
277 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); | 278 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); |
278 | let from_scope = self[module].scope.get(name, shadow); | 279 | let from_scope = self[module].scope.get(name); |
280 | let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none); | ||
281 | let from_scope_or_builtin = match shadow { | ||
282 | BuiltinShadowMode::Module => from_scope.or(from_builtin), | ||
283 | BuiltinShadowMode::Other => { | ||
284 | if let Some(ModuleDefId::ModuleId(_)) = from_scope.take_types() { | ||
285 | from_builtin.or(from_scope) | ||
286 | } else { | ||
287 | from_scope.or(from_builtin) | ||
288 | } | ||
289 | } | ||
290 | }; | ||
279 | let from_extern_prelude = self | 291 | let from_extern_prelude = self |
280 | .extern_prelude | 292 | .extern_prelude |
281 | .get(name) | 293 | .get(name) |
282 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); | 294 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); |
283 | let from_prelude = self.resolve_in_prelude(db, name, shadow); | 295 | let from_prelude = self.resolve_in_prelude(db, name); |
284 | 296 | ||
285 | from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude) | 297 | from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) |
286 | } | 298 | } |
287 | 299 | ||
288 | fn resolve_name_in_crate_root_or_extern_prelude( | 300 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs { |
289 | &self, | 301 | let from_crate_root = self[self.root].scope.get(name); |
290 | name: &Name, | ||
291 | shadow: BuiltinShadowMode, | ||
292 | ) -> PerNs { | ||
293 | let from_crate_root = self[self.root].scope.get(name, shadow); | ||
294 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | 302 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); |
295 | 303 | ||
296 | from_crate_root.or(from_extern_prelude) | 304 | from_crate_root.or(from_extern_prelude) |
297 | } | 305 | } |
298 | 306 | ||
299 | fn resolve_in_prelude( | 307 | fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> PerNs { |
300 | &self, | ||
301 | db: &impl DefDatabase, | ||
302 | name: &Name, | ||
303 | shadow: BuiltinShadowMode, | ||
304 | ) -> PerNs { | ||
305 | if let Some(prelude) = self.prelude { | 308 | if let Some(prelude) = self.prelude { |
306 | let keep; | 309 | let keep; |
307 | let def_map = if prelude.krate == self.krate { | 310 | let def_map = if prelude.krate == self.krate { |
@@ -311,7 +314,7 @@ impl CrateDefMap { | |||
311 | keep = db.crate_def_map(prelude.krate); | 314 | keep = db.crate_def_map(prelude.krate); |
312 | &keep | 315 | &keep |
313 | }; | 316 | }; |
314 | def_map[prelude.local_id].scope.get(name, shadow) | 317 | def_map[prelude.local_id].scope.get(name) |
315 | } else { | 318 | } else { |
316 | PerNs::none() | 319 | PerNs::none() |
317 | } | 320 | } |
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 5365b80e2..9dd4fa555 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs | |||
@@ -15,7 +15,7 @@ use crate::{ | |||
15 | db::DefDatabase, | 15 | db::DefDatabase, |
16 | expr::{ExprId, PatId}, | 16 | expr::{ExprId, PatId}, |
17 | generics::GenericParams, | 17 | generics::GenericParams, |
18 | item_scope::BuiltinShadowMode, | 18 | item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, |
19 | nameres::CrateDefMap, | 19 | nameres::CrateDefMap, |
20 | path::{ModPath, PathKind}, | 20 | path::{ModPath, PathKind}, |
21 | per_ns::PerNs, | 21 | per_ns::PerNs, |
@@ -193,7 +193,7 @@ impl Resolver { | |||
193 | return Some((res, idx)); | 193 | return Some((res, idx)); |
194 | } | 194 | } |
195 | Scope::LocalItemsScope(body) => { | 195 | Scope::LocalItemsScope(body) => { |
196 | let def = body.item_scope.get(first_name, BuiltinShadowMode::Other); | 196 | let def = body.item_scope.get(first_name); |
197 | if let Some(res) = to_type_ns(def) { | 197 | if let Some(res) = to_type_ns(def) { |
198 | return Some((res, None)); | 198 | return Some((res, None)); |
199 | } | 199 | } |
@@ -335,8 +335,10 @@ impl Resolver { | |||
335 | }; | 335 | }; |
336 | } | 336 | } |
337 | Scope::LocalItemsScope(body) => { | 337 | Scope::LocalItemsScope(body) => { |
338 | let def = body.item_scope.get(first_name, BuiltinShadowMode::Other); | 338 | // we don't bother looking in the builtin scope here because there are no builtin values |
339 | if let Some(res) = to_value_ns(def) { | 339 | let def = to_value_ns(body.item_scope.get(first_name)); |
340 | |||
341 | if let Some(res) = def { | ||
340 | return Some(ResolveValueResult::ValueNs(res)); | 342 | return Some(ResolveValueResult::ValueNs(res)); |
341 | } | 343 | } |
342 | } | 344 | } |
@@ -476,6 +478,9 @@ impl Scope { | |||
476 | m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { | 478 | m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { |
477 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); | 479 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); |
478 | }); | 480 | }); |
481 | BUILTIN_SCOPE.iter().for_each(|(name, &def)| { | ||
482 | f(name.clone(), ScopeDef::PerNs(def)); | ||
483 | }); | ||
479 | if let Some(prelude) = m.crate_def_map.prelude { | 484 | if let Some(prelude) = m.crate_def_map.prelude { |
480 | let prelude_def_map = db.crate_def_map(prelude.krate); | 485 | let prelude_def_map = db.crate_def_map(prelude.krate); |
481 | prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { | 486 | prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { |