diff options
author | Aleksey Kladov <[email protected]> | 2019-11-08 21:17:17 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-11-08 21:17:17 +0000 |
commit | 53945841bea2d8016586889ccd8f8bd8c487547b (patch) | |
tree | 71b497f6375149ff424d6440889111eabeec7b98 /crates/ra_hir_def/src/nameres.rs | |
parent | 785887b3829e2753cefcde48f527cf829fd051f4 (diff) |
Extract path resolution submodule
Diffstat (limited to 'crates/ra_hir_def/src/nameres.rs')
-rw-r--r-- | crates/ra_hir_def/src/nameres.rs | 247 |
1 files changed, 6 insertions, 241 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; | |||
52 | pub mod per_ns; | 52 | pub mod per_ns; |
53 | mod collector; | 53 | mod collector; |
54 | mod mod_resolution; | 54 | mod mod_resolution; |
55 | mod path_resolution; | ||
55 | 56 | ||
56 | #[cfg(test)] | 57 | #[cfg(test)] |
57 | mod tests; | 58 | mod tests; |
@@ -65,14 +66,15 @@ use ra_db::{CrateId, Edition, FileId}; | |||
65 | use ra_prof::profile; | 66 | use ra_prof::profile; |
66 | use ra_syntax::ast; | 67 | use ra_syntax::ast; |
67 | use rustc_hash::{FxHashMap, FxHashSet}; | 68 | use rustc_hash::{FxHashMap, FxHashSet}; |
68 | use test_utils::tested_by; | ||
69 | 69 | ||
70 | use crate::{ | 70 | use 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)] | ||
199 | struct ResolvePathResult { | ||
200 | resolved_def: PerNs, | ||
201 | segment_index: Option<usize>, | ||
202 | reached_fixedpoint: ReachedFixedPoint, | ||
203 | } | ||
204 | |||
205 | impl 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)] | ||
220 | enum ResolveMode { | ||
221 | Import, | ||
222 | Other, | ||
223 | } | ||
224 | |||
225 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
226 | enum ReachedFixedPoint { | ||
227 | Yes, | ||
228 | No, | ||
229 | } | ||
230 | |||
231 | impl CrateDefMap { | 200 | impl 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 | ||
499 | mod diagnostics { | 264 | mod diagnostics { |