aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-11-08 21:17:17 +0000
committerAleksey Kladov <[email protected]>2019-11-08 21:17:17 +0000
commit53945841bea2d8016586889ccd8f8bd8c487547b (patch)
tree71b497f6375149ff424d6440889111eabeec7b98 /crates/ra_hir_def
parent785887b3829e2753cefcde48f527cf829fd051f4 (diff)
Extract path resolution submodule
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/src/nameres.rs247
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs4
-rw-r--r--crates/ra_hir_def/src/nameres/path_resolution.rs261
3 files changed, 269 insertions, 243 deletions
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index fb3ba5305..115b0264c 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -52,6 +52,7 @@ pub mod raw;
52pub mod per_ns; 52pub mod per_ns;
53mod collector; 53mod collector;
54mod mod_resolution; 54mod mod_resolution;
55mod path_resolution;
55 56
56#[cfg(test)] 57#[cfg(test)]
57mod tests; 58mod tests;
@@ -65,14 +66,15 @@ use ra_db::{CrateId, Edition, FileId};
65use ra_prof::profile; 66use ra_prof::profile;
66use ra_syntax::ast; 67use ra_syntax::ast;
67use rustc_hash::{FxHashMap, FxHashSet}; 68use rustc_hash::{FxHashMap, FxHashSet};
68use test_utils::tested_by;
69 69
70use crate::{ 70use crate::{
71 builtin_type::BuiltinType, 71 builtin_type::BuiltinType,
72 db::DefDatabase2, 72 db::DefDatabase2,
73 nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId}, 73 nameres::{
74 path::{Path, PathKind}, 74 diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
75 AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId, 75 },
76 path::Path,
77 AstId, CrateModuleId, ModuleDefId, ModuleId, TraitId,
76}; 78};
77 79
78/// Contains all top-level defs from a macro-expanded crate 80/// Contains all top-level defs from a macro-expanded crate
@@ -195,39 +197,6 @@ pub struct Resolution {
195 pub import: Option<ImportId>, 197 pub import: Option<ImportId>,
196} 198}
197 199
198#[derive(Debug, Clone)]
199struct ResolvePathResult {
200 resolved_def: PerNs,
201 segment_index: Option<usize>,
202 reached_fixedpoint: ReachedFixedPoint,
203}
204
205impl ResolvePathResult {
206 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
207 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
208 }
209
210 fn with(
211 resolved_def: PerNs,
212 reached_fixedpoint: ReachedFixedPoint,
213 segment_index: Option<usize>,
214 ) -> ResolvePathResult {
215 ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220enum ResolveMode {
221 Import,
222 Other,
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226enum ReachedFixedPoint {
227 Yes,
228 No,
229}
230
231impl CrateDefMap { 200impl CrateDefMap {
232 pub(crate) fn crate_def_map_query( 201 pub(crate) fn crate_def_map_query(
233 // Note that this doesn't have `+ AstDatabase`! 202 // Note that this doesn't have `+ AstDatabase`!
@@ -290,210 +259,6 @@ impl CrateDefMap {
290 let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path); 259 let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
291 (res.resolved_def, res.segment_index) 260 (res.resolved_def, res.segment_index)
292 } 261 }
293
294 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
295 // the result.
296 fn resolve_path_fp_with_macro(
297 &self,
298 db: &impl DefDatabase2,
299 mode: ResolveMode,
300 original_module: CrateModuleId,
301 path: &Path,
302 ) -> ResolvePathResult {
303 let mut segments = path.segments.iter().enumerate();
304 let mut curr_per_ns: PerNs = match path.kind {
305 PathKind::DollarCrate(krate) => {
306 if krate == self.krate {
307 tested_by!(macro_dollar_crate_self);
308 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
309 } else {
310 let def_map = db.crate_def_map(krate);
311 let module = ModuleId { krate, module_id: def_map.root };
312 tested_by!(macro_dollar_crate_other);
313 PerNs::types(module.into())
314 }
315 }
316 PathKind::Crate => {
317 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
318 }
319 PathKind::Self_ => {
320 PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
321 }
322 // plain import or absolute path in 2015: crate-relative with
323 // fallback to extern prelude (with the simplification in
324 // rust-lang/rust#57745)
325 // FIXME there must be a nicer way to write this condition
326 PathKind::Plain | PathKind::Abs
327 if self.edition == Edition::Edition2015
328 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
329 {
330 let segment = match segments.next() {
331 Some((_, segment)) => segment,
332 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
333 };
334 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
335 self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
336 }
337 PathKind::Plain => {
338 let segment = match segments.next() {
339 Some((_, segment)) => segment,
340 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
341 };
342 log::debug!("resolving {:?} in module", segment);
343 self.resolve_name_in_module(db, original_module, &segment.name)
344 }
345 PathKind::Super => {
346 if let Some(p) = self.modules[original_module].parent {
347 PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
348 } else {
349 log::debug!("super path in root module");
350 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
351 }
352 }
353 PathKind::Abs => {
354 // 2018-style absolute path -- only extern prelude
355 let segment = match segments.next() {
356 Some((_, segment)) => segment,
357 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
358 };
359 if let Some(def) = self.extern_prelude.get(&segment.name) {
360 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
361 PerNs::types(*def)
362 } else {
363 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
364 }
365 }
366 PathKind::Type(_) => {
367 // This is handled in `infer::infer_path_expr`
368 // The result returned here does not matter
369 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
370 }
371 };
372
373 for (i, segment) in segments {
374 let curr = match curr_per_ns.take_types() {
375 Some(r) => r,
376 None => {
377 // we still have path segments left, but the path so far
378 // didn't resolve in the types namespace => no resolution
379 // (don't break here because `curr_per_ns` might contain
380 // something in the value namespace, and it would be wrong
381 // to return that)
382 return ResolvePathResult::empty(ReachedFixedPoint::No);
383 }
384 };
385 // resolve segment in curr
386
387 curr_per_ns = match curr {
388 ModuleDefId::ModuleId(module) => {
389 if module.krate != self.krate {
390 let path =
391 Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
392 log::debug!("resolving {:?} in other crate", path);
393 let defp_map = db.crate_def_map(module.krate);
394 let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
395 return ResolvePathResult::with(
396 def,
397 ReachedFixedPoint::Yes,
398 s.map(|s| s + i),
399 );
400 }
401
402 // Since it is a qualified path here, it should not contains legacy macros
403 match self[module.module_id].scope.get(&segment.name) {
404 Some(res) => res.def,
405 _ => {
406 log::debug!("path segment {:?} not found", segment.name);
407 return ResolvePathResult::empty(ReachedFixedPoint::No);
408 }
409 }
410 }
411 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
412 // enum variant
413 tested_by!(can_import_enum_variant);
414 let enum_data = db.enum_data(e);
415 match enum_data.variant(&segment.name) {
416 Some(local_id) => {
417 let variant = EnumVariantId { parent: e, local_id };
418 PerNs::both(variant.into(), variant.into())
419 }
420 None => {
421 return ResolvePathResult::with(
422 PerNs::types(e.into()),
423 ReachedFixedPoint::Yes,
424 Some(i),
425 );
426 }
427 }
428 }
429 s => {
430 // could be an inherent method call in UFCS form
431 // (`Struct::method`), or some other kind of associated item
432 log::debug!(
433 "path segment {:?} resolved to non-module {:?}, but is not last",
434 segment.name,
435 curr,
436 );
437
438 return ResolvePathResult::with(
439 PerNs::types(s),
440 ReachedFixedPoint::Yes,
441 Some(i),
442 );
443 }
444 };
445 }
446 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
447 }
448
449 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
450 let from_crate_root =
451 self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
452 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
453
454 from_crate_root.or(from_extern_prelude)
455 }
456
457 fn resolve_name_in_module(
458 &self,
459 db: &impl DefDatabase2,
460 module: CrateModuleId,
461 name: &Name,
462 ) -> PerNs {
463 // Resolve in:
464 // - legacy scope of macro
465 // - current module / scope
466 // - extern prelude
467 // - std prelude
468 let from_legacy_macro =
469 self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
470 let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
471 let from_extern_prelude =
472 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
473 let from_prelude = self.resolve_in_prelude(db, name);
474
475 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
476 }
477
478 fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
479 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
480 }
481
482 fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
483 if let Some(prelude) = self.prelude {
484 let keep;
485 let def_map = if prelude.krate == self.krate {
486 self
487 } else {
488 // Extend lifetime
489 keep = db.crate_def_map(prelude.krate);
490 &keep
491 };
492 def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
493 } else {
494 PerNs::none()
495 }
496 }
497} 262}
498 263
499mod diagnostics { 264mod diagnostics {
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 9ab378d54..aacd50df8 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -14,8 +14,8 @@ use crate::{
14 attr::Attr, 14 attr::Attr,
15 db::DefDatabase2, 15 db::DefDatabase2,
16 nameres::{ 16 nameres::{
17 diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap, 17 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
18 ModuleData, ReachedFixedPoint, Resolution, ResolveMode, 18 per_ns::PerNs, raw, CrateDefMap, ModuleData, Resolution, ResolveMode,
19 }, 19 },
20 path::{Path, PathKind}, 20 path::{Path, PathKind},
21 AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId, 21 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
index 000000000..95692f826
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/path_resolution.rs
@@ -0,0 +1,261 @@
1//! This modules implements a function to resolve a path `foo::bar::baz` to a
2//! def, which is used within the name resolution.
3//!
4//! When name resolution is finished, the result of resolving a path is either
5//! `Some(def)` or `None`. However, when we are in process of resolving imports
6//! or macros, there's a third possibility:
7//!
8//! I can't resolve this path right now, but I might be resolve this path
9//! later, when more macros are expanded.
10//!
11//! `ReachedFixedPoint` signals about this.
12
13use hir_expand::name::Name;
14use ra_db::Edition;
15use test_utils::tested_by;
16
17use crate::{
18 db::DefDatabase2,
19 nameres::{per_ns::PerNs, CrateDefMap},
20 path::{Path, PathKind},
21 AdtId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId,
22};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub(super) enum ResolveMode {
26 Import,
27 Other,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub(super) enum ReachedFixedPoint {
32 Yes,
33 No,
34}
35
36#[derive(Debug, Clone)]
37pub(super) struct ResolvePathResult {
38 pub(super) resolved_def: PerNs,
39 pub(super) segment_index: Option<usize>,
40 pub(super) reached_fixedpoint: ReachedFixedPoint,
41}
42
43impl ResolvePathResult {
44 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
45 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
46 }
47
48 fn with(
49 resolved_def: PerNs,
50 reached_fixedpoint: ReachedFixedPoint,
51 segment_index: Option<usize>,
52 ) -> ResolvePathResult {
53 ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
54 }
55}
56
57impl CrateDefMap {
58 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
59 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
60 }
61
62 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
63 // the result.
64 pub(super) fn resolve_path_fp_with_macro(
65 &self,
66 db: &impl DefDatabase2,
67 mode: ResolveMode,
68 original_module: CrateModuleId,
69 path: &Path,
70 ) -> ResolvePathResult {
71 let mut segments = path.segments.iter().enumerate();
72 let mut curr_per_ns: PerNs = match path.kind {
73 PathKind::DollarCrate(krate) => {
74 if krate == self.krate {
75 tested_by!(macro_dollar_crate_self);
76 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
77 } else {
78 let def_map = db.crate_def_map(krate);
79 let module = ModuleId { krate, module_id: def_map.root };
80 tested_by!(macro_dollar_crate_other);
81 PerNs::types(module.into())
82 }
83 }
84 PathKind::Crate => {
85 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
86 }
87 PathKind::Self_ => {
88 PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
89 }
90 // plain import or absolute path in 2015: crate-relative with
91 // fallback to extern prelude (with the simplification in
92 // rust-lang/rust#57745)
93 // FIXME there must be a nicer way to write this condition
94 PathKind::Plain | PathKind::Abs
95 if self.edition == Edition::Edition2015
96 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
97 {
98 let segment = match segments.next() {
99 Some((_, segment)) => segment,
100 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
101 };
102 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
103 self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
104 }
105 PathKind::Plain => {
106 let segment = match segments.next() {
107 Some((_, segment)) => segment,
108 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
109 };
110 log::debug!("resolving {:?} in module", segment);
111 self.resolve_name_in_module(db, original_module, &segment.name)
112 }
113 PathKind::Super => {
114 if let Some(p) = self.modules[original_module].parent {
115 PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
116 } else {
117 log::debug!("super path in root module");
118 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
119 }
120 }
121 PathKind::Abs => {
122 // 2018-style absolute path -- only extern prelude
123 let segment = match segments.next() {
124 Some((_, segment)) => segment,
125 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
126 };
127 if let Some(def) = self.extern_prelude.get(&segment.name) {
128 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
129 PerNs::types(*def)
130 } else {
131 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
132 }
133 }
134 PathKind::Type(_) => {
135 // This is handled in `infer::infer_path_expr`
136 // The result returned here does not matter
137 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
138 }
139 };
140
141 for (i, segment) in segments {
142 let curr = match curr_per_ns.take_types() {
143 Some(r) => r,
144 None => {
145 // we still have path segments left, but the path so far
146 // didn't resolve in the types namespace => no resolution
147 // (don't break here because `curr_per_ns` might contain
148 // something in the value namespace, and it would be wrong
149 // to return that)
150 return ResolvePathResult::empty(ReachedFixedPoint::No);
151 }
152 };
153 // resolve segment in curr
154
155 curr_per_ns = match curr {
156 ModuleDefId::ModuleId(module) => {
157 if module.krate != self.krate {
158 let path =
159 Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
160 log::debug!("resolving {:?} in other crate", path);
161 let defp_map = db.crate_def_map(module.krate);
162 let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
163 return ResolvePathResult::with(
164 def,
165 ReachedFixedPoint::Yes,
166 s.map(|s| s + i),
167 );
168 }
169
170 // Since it is a qualified path here, it should not contains legacy macros
171 match self[module.module_id].scope.get(&segment.name) {
172 Some(res) => res.def,
173 _ => {
174 log::debug!("path segment {:?} not found", segment.name);
175 return ResolvePathResult::empty(ReachedFixedPoint::No);
176 }
177 }
178 }
179 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
180 // enum variant
181 tested_by!(can_import_enum_variant);
182 let enum_data = db.enum_data(e);
183 match enum_data.variant(&segment.name) {
184 Some(local_id) => {
185 let variant = EnumVariantId { parent: e, local_id };
186 PerNs::both(variant.into(), variant.into())
187 }
188 None => {
189 return ResolvePathResult::with(
190 PerNs::types(e.into()),
191 ReachedFixedPoint::Yes,
192 Some(i),
193 );
194 }
195 }
196 }
197 s => {
198 // could be an inherent method call in UFCS form
199 // (`Struct::method`), or some other kind of associated item
200 log::debug!(
201 "path segment {:?} resolved to non-module {:?}, but is not last",
202 segment.name,
203 curr,
204 );
205
206 return ResolvePathResult::with(
207 PerNs::types(s),
208 ReachedFixedPoint::Yes,
209 Some(i),
210 );
211 }
212 };
213 }
214 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
215 }
216
217 fn resolve_name_in_module(
218 &self,
219 db: &impl DefDatabase2,
220 module: CrateModuleId,
221 name: &Name,
222 ) -> PerNs {
223 // Resolve in:
224 // - legacy scope of macro
225 // - current module / scope
226 // - extern prelude
227 // - std prelude
228 let from_legacy_macro =
229 self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
230 let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
231 let from_extern_prelude =
232 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
233 let from_prelude = self.resolve_in_prelude(db, name);
234
235 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
236 }
237
238 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
239 let from_crate_root =
240 self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
241 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
242
243 from_crate_root.or(from_extern_prelude)
244 }
245
246 fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
247 if let Some(prelude) = self.prelude {
248 let keep;
249 let def_map = if prelude.krate == self.krate {
250 self
251 } else {
252 // Extend lifetime
253 keep = db.crate_def_map(prelude.krate);
254 &keep
255 };
256 def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
257 } else {
258 PerNs::none()
259 }
260 }
261}