diff options
author | Ville Penttinen <[email protected]> | 2019-02-21 10:04:14 +0000 |
---|---|---|
committer | Ville Penttinen <[email protected]> | 2019-02-21 10:25:55 +0000 |
commit | 816971ebc9207c5fb5779d448613dd171c27f398 (patch) | |
tree | fe49d209a852bb1b4a51eea9d40449dcf2845209 | |
parent | c84561bb624280b84eb2fe6c6b2a6b9fe3f1dbf7 (diff) |
Implement basic support for Associated Methods and Constants
This is done in `infer_path_expr`. When `Resolver::resolve_path` returns
`PartiallyResolved`, we use the returned `Resolution` together with the given
`segment_index` to check if we can find something matching the segment at
segment_index in the impls for that particular type.
-rw-r--r-- | crates/ra_hir/src/impl_block.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 78 | ||||
-rw-r--r-- | crates/ra_hir/src/resolve.rs | 46 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 108 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap | 14 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_enum.snap | 20 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_struct.snap | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_with_modules.snap | 23 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 133 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_path.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 20 |
13 files changed, 430 insertions, 50 deletions
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 7ecf8c368..f4dccf165 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs | |||
@@ -88,7 +88,7 @@ impl ImplBlock { | |||
88 | if let Some(TypeRef::Path(path)) = self.target_trait_ref(db) { | 88 | if let Some(TypeRef::Path(path)) = self.target_trait_ref(db) { |
89 | let resolver = self.resolver(db); | 89 | let resolver = self.resolver(db); |
90 | if let Some(Resolution::Def(ModuleDef::Trait(tr))) = | 90 | if let Some(Resolution::Def(ModuleDef::Trait(tr))) = |
91 | resolver.resolve_path(db, &path).take_types() | 91 | resolver.resolve_path(db, &path).into_per_ns().take_types() |
92 | { | 92 | { |
93 | return Some(tr); | 93 | return Some(tr); |
94 | } | 94 | } |
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index bd920bfea..b78a178c1 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -119,6 +119,10 @@ impl<T> PerNs<T> { | |||
119 | self.types.is_some() && self.values.is_some() | 119 | self.types.is_some() && self.values.is_some() |
120 | } | 120 | } |
121 | 121 | ||
122 | pub fn is_values(&self) -> bool { | ||
123 | self.values.is_some() && self.types.is_none() | ||
124 | } | ||
125 | |||
122 | pub fn take(self, namespace: Namespace) -> Option<T> { | 126 | pub fn take(self, namespace: Namespace) -> Option<T> { |
123 | match namespace { | 127 | match namespace { |
124 | Namespace::Types => self.types, | 128 | Namespace::Types => self.types, |
@@ -297,7 +301,14 @@ where | |||
297 | ); | 301 | ); |
298 | (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes }) | 302 | (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes }) |
299 | } else { | 303 | } else { |
300 | self.result.resolve_path_fp(self.db, ResolveMode::Import, original_module, &import.path) | 304 | let res = self.result.resolve_path_fp( |
305 | self.db, | ||
306 | ResolveMode::Import, | ||
307 | original_module, | ||
308 | &import.path, | ||
309 | ); | ||
310 | |||
311 | (res.module, res.reached_fixedpoint) | ||
301 | }; | 312 | }; |
302 | 313 | ||
303 | if reached_fixedpoint != ReachedFixedPoint::Yes { | 314 | if reached_fixedpoint != ReachedFixedPoint::Yes { |
@@ -435,6 +446,27 @@ where | |||
435 | } | 446 | } |
436 | } | 447 | } |
437 | 448 | ||
449 | #[derive(Debug, Clone)] | ||
450 | pub struct ResolvePathResult { | ||
451 | pub(crate) module: PerNs<ModuleDef>, | ||
452 | pub(crate) segment_index: Option<usize>, | ||
453 | reached_fixedpoint: ReachedFixedPoint, | ||
454 | } | ||
455 | |||
456 | impl ResolvePathResult { | ||
457 | fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { | ||
458 | ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None) | ||
459 | } | ||
460 | |||
461 | fn with( | ||
462 | module: PerNs<ModuleDef>, | ||
463 | reached_fixedpoint: ReachedFixedPoint, | ||
464 | segment_index: Option<usize>, | ||
465 | ) -> ResolvePathResult { | ||
466 | ResolvePathResult { module, reached_fixedpoint, segment_index } | ||
467 | } | ||
468 | } | ||
469 | |||
438 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 470 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
439 | enum ResolveMode { | 471 | enum ResolveMode { |
440 | Import, | 472 | Import, |
@@ -468,8 +500,9 @@ impl ItemMap { | |||
468 | db: &impl PersistentHirDatabase, | 500 | db: &impl PersistentHirDatabase, |
469 | original_module: Module, | 501 | original_module: Module, |
470 | path: &Path, | 502 | path: &Path, |
471 | ) -> PerNs<ModuleDef> { | 503 | ) -> (PerNs<ModuleDef>, Option<usize>) { |
472 | self.resolve_path_fp(db, ResolveMode::Other, original_module, path).0 | 504 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); |
505 | (res.module, res.segment_index) | ||
473 | } | 506 | } |
474 | 507 | ||
475 | fn resolve_in_prelude( | 508 | fn resolve_in_prelude( |
@@ -534,7 +567,7 @@ impl ItemMap { | |||
534 | mode: ResolveMode, | 567 | mode: ResolveMode, |
535 | original_module: Module, | 568 | original_module: Module, |
536 | path: &Path, | 569 | path: &Path, |
537 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | 570 | ) -> ResolvePathResult { |
538 | let mut segments = path.segments.iter().enumerate(); | 571 | let mut segments = path.segments.iter().enumerate(); |
539 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { | 572 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { |
540 | PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), | 573 | PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), |
@@ -549,7 +582,7 @@ impl ItemMap { | |||
549 | { | 582 | { |
550 | let segment = match segments.next() { | 583 | let segment = match segments.next() { |
551 | Some((_, segment)) => segment, | 584 | Some((_, segment)) => segment, |
552 | None => return (PerNs::none(), ReachedFixedPoint::Yes), | 585 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
553 | }; | 586 | }; |
554 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | 587 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); |
555 | self.resolve_name_in_crate_root_or_extern_prelude( | 588 | self.resolve_name_in_crate_root_or_extern_prelude( |
@@ -561,7 +594,7 @@ impl ItemMap { | |||
561 | PathKind::Plain => { | 594 | PathKind::Plain => { |
562 | let segment = match segments.next() { | 595 | let segment = match segments.next() { |
563 | Some((_, segment)) => segment, | 596 | Some((_, segment)) => segment, |
564 | None => return (PerNs::none(), ReachedFixedPoint::Yes), | 597 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
565 | }; | 598 | }; |
566 | log::debug!("resolving {:?} in module", segment); | 599 | log::debug!("resolving {:?} in module", segment); |
567 | self.resolve_name_in_module(db, original_module, &segment.name) | 600 | self.resolve_name_in_module(db, original_module, &segment.name) |
@@ -571,20 +604,20 @@ impl ItemMap { | |||
571 | PerNs::types(p.into()) | 604 | PerNs::types(p.into()) |
572 | } else { | 605 | } else { |
573 | log::debug!("super path in root module"); | 606 | log::debug!("super path in root module"); |
574 | return (PerNs::none(), ReachedFixedPoint::Yes); | 607 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); |
575 | } | 608 | } |
576 | } | 609 | } |
577 | PathKind::Abs => { | 610 | PathKind::Abs => { |
578 | // 2018-style absolute path -- only extern prelude | 611 | // 2018-style absolute path -- only extern prelude |
579 | let segment = match segments.next() { | 612 | let segment = match segments.next() { |
580 | Some((_, segment)) => segment, | 613 | Some((_, segment)) => segment, |
581 | None => return (PerNs::none(), ReachedFixedPoint::Yes), | 614 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
582 | }; | 615 | }; |
583 | if let Some(def) = self.extern_prelude.get(&segment.name) { | 616 | if let Some(def) = self.extern_prelude.get(&segment.name) { |
584 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); | 617 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); |
585 | PerNs::types(*def) | 618 | PerNs::types(*def) |
586 | } else { | 619 | } else { |
587 | return (PerNs::none(), ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude | 620 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude |
588 | } | 621 | } |
589 | } | 622 | } |
590 | }; | 623 | }; |
@@ -598,7 +631,7 @@ impl ItemMap { | |||
598 | // (don't break here because `curr_per_ns` might contain | 631 | // (don't break here because `curr_per_ns` might contain |
599 | // something in the value namespace, and it would be wrong | 632 | // something in the value namespace, and it would be wrong |
600 | // to return that) | 633 | // to return that) |
601 | return (PerNs::none(), ReachedFixedPoint::No); | 634 | return ResolvePathResult::empty(ReachedFixedPoint::No); |
602 | } | 635 | } |
603 | }; | 636 | }; |
604 | // resolve segment in curr | 637 | // resolve segment in curr |
@@ -612,15 +645,15 @@ impl ItemMap { | |||
612 | }; | 645 | }; |
613 | log::debug!("resolving {:?} in other crate", path); | 646 | log::debug!("resolving {:?} in other crate", path); |
614 | let item_map = db.item_map(module.krate); | 647 | let item_map = db.item_map(module.krate); |
615 | let def = item_map.resolve_path(db, *module, &path); | 648 | let (def, s) = item_map.resolve_path(db, *module, &path); |
616 | return (def, ReachedFixedPoint::Yes); | 649 | return ResolvePathResult::with(def, ReachedFixedPoint::Yes, s); |
617 | } | 650 | } |
618 | 651 | ||
619 | match self[module.module_id].items.get(&segment.name) { | 652 | match self[module.module_id].items.get(&segment.name) { |
620 | Some(res) if !res.def.is_none() => res.def, | 653 | Some(res) if !res.def.is_none() => res.def, |
621 | _ => { | 654 | _ => { |
622 | log::debug!("path segment {:?} not found", segment.name); | 655 | log::debug!("path segment {:?} not found", segment.name); |
623 | return (PerNs::none(), ReachedFixedPoint::No); | 656 | return ResolvePathResult::empty(ReachedFixedPoint::No); |
624 | } | 657 | } |
625 | } | 658 | } |
626 | } | 659 | } |
@@ -629,9 +662,22 @@ impl ItemMap { | |||
629 | tested_by!(item_map_enum_importing); | 662 | tested_by!(item_map_enum_importing); |
630 | match e.variant(db, &segment.name) { | 663 | match e.variant(db, &segment.name) { |
631 | Some(variant) => PerNs::both(variant.into(), variant.into()), | 664 | Some(variant) => PerNs::both(variant.into(), variant.into()), |
632 | None => PerNs::none(), | 665 | None => { |
666 | return ResolvePathResult::with( | ||
667 | PerNs::types((*e).into()), | ||
668 | ReachedFixedPoint::Yes, | ||
669 | Some(i), | ||
670 | ); | ||
671 | } | ||
633 | } | 672 | } |
634 | } | 673 | } |
674 | ModuleDef::Struct(s) => { | ||
675 | return ResolvePathResult::with( | ||
676 | PerNs::types((*s).into()), | ||
677 | ReachedFixedPoint::Yes, | ||
678 | Some(i), | ||
679 | ); | ||
680 | } | ||
635 | _ => { | 681 | _ => { |
636 | // could be an inherent method call in UFCS form | 682 | // could be an inherent method call in UFCS form |
637 | // (`Struct::method`), or some other kind of associated | 683 | // (`Struct::method`), or some other kind of associated |
@@ -641,11 +687,11 @@ impl ItemMap { | |||
641 | segment.name, | 687 | segment.name, |
642 | curr, | 688 | curr, |
643 | ); | 689 | ); |
644 | return (PerNs::none(), ReachedFixedPoint::Yes); | 690 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); |
645 | } | 691 | } |
646 | }; | 692 | }; |
647 | } | 693 | } |
648 | (curr_per_ns, ReachedFixedPoint::Yes) | 694 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) |
649 | } | 695 | } |
650 | } | 696 | } |
651 | 697 | ||
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 91a531801..00b55eae9 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs | |||
@@ -33,6 +33,32 @@ pub(crate) struct ExprScope { | |||
33 | } | 33 | } |
34 | 34 | ||
35 | #[derive(Debug, Clone)] | 35 | #[derive(Debug, Clone)] |
36 | pub enum PathResult { | ||
37 | /// Path was fully resolved | ||
38 | FullyResolved(PerNs<Resolution>), | ||
39 | /// Path was partially resolved, first element contains the resolution | ||
40 | /// second contains the index in the Path.segments which we were unable to resolve | ||
41 | PartiallyResolved(PerNs<Resolution>, usize), | ||
42 | } | ||
43 | |||
44 | impl PathResult { | ||
45 | pub fn segment_index(&self) -> Option<usize> { | ||
46 | match self { | ||
47 | PathResult::FullyResolved(_) => None, | ||
48 | PathResult::PartiallyResolved(_, ref i) => Some(*i), | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /// Consumes `PathResult` and returns the contained `PerNs<Resolution>` | ||
53 | pub fn into_per_ns(self) -> PerNs<Resolution> { | ||
54 | match self { | ||
55 | PathResult::FullyResolved(def) => def, | ||
56 | PathResult::PartiallyResolved(def, _) => def, | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | #[derive(Debug, Clone)] | ||
36 | pub(crate) enum Scope { | 62 | pub(crate) enum Scope { |
37 | /// All the items and imported names of a module | 63 | /// All the items and imported names of a module |
38 | ModuleScope(ModuleItemMap), | 64 | ModuleScope(ModuleItemMap), |
@@ -67,18 +93,26 @@ impl Resolver { | |||
67 | resolution | 93 | resolution |
68 | } | 94 | } |
69 | 95 | ||
70 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> { | 96 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PathResult { |
97 | use self::PathResult::*; | ||
71 | if let Some(name) = path.as_ident() { | 98 | if let Some(name) = path.as_ident() { |
72 | self.resolve_name(db, name) | 99 | FullyResolved(self.resolve_name(db, name)) |
73 | } else if path.is_self() { | 100 | } else if path.is_self() { |
74 | self.resolve_name(db, &Name::self_param()) | 101 | FullyResolved(self.resolve_name(db, &Name::self_param())) |
75 | } else { | 102 | } else { |
76 | let (item_map, module) = match self.module() { | 103 | let (item_map, module) = match self.module() { |
77 | Some(m) => m, | 104 | Some(m) => m, |
78 | _ => return PerNs::none(), | 105 | _ => return FullyResolved(PerNs::none()), |
79 | }; | 106 | }; |
80 | let module_res = item_map.resolve_path(db, module, path); | 107 | let (module_res, segment_index) = item_map.resolve_path(db, module, path); |
81 | module_res.map(Resolution::Def) | 108 | |
109 | let def = module_res.map(Resolution::Def); | ||
110 | |||
111 | if let Some(index) = segment_index { | ||
112 | PartiallyResolved(def, index) | ||
113 | } else { | ||
114 | FullyResolved(def) | ||
115 | } | ||
82 | } | 116 | } |
83 | } | 117 | } |
84 | 118 | ||
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d4d896673..2751ab3ab 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -32,17 +32,20 @@ use rustc_hash::FxHashMap; | |||
32 | 32 | ||
33 | use test_utils::tested_by; | 33 | use test_utils::tested_by; |
34 | 34 | ||
35 | use ra_syntax::ast::NameOwner; | ||
36 | |||
35 | use crate::{ | 37 | use crate::{ |
36 | Function, Struct, StructField, Enum, EnumVariant, Path, Name, | 38 | Function, Struct, StructField, Enum, EnumVariant, Path, Name, |
39 | Const, | ||
37 | FnSignature, ModuleDef, AdtDef, | 40 | FnSignature, ModuleDef, AdtDef, |
38 | HirDatabase, | 41 | HirDatabase, |
39 | type_ref::{TypeRef, Mutability}, | 42 | type_ref::{TypeRef, Mutability}, |
40 | name::KnownName, | 43 | name::{KnownName, AsName}, |
41 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, | 44 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, |
42 | generics::GenericParams, | 45 | generics::GenericParams, |
43 | path::GenericArg, | 46 | path::GenericArg, |
44 | adt::VariantDef, | 47 | adt::VariantDef, |
45 | resolve::{Resolver, Resolution}, nameres::Namespace | 48 | resolve::{Resolver, Resolution, PathResult}, nameres::Namespace |
46 | }; | 49 | }; |
47 | 50 | ||
48 | /// The ID of a type variable. | 51 | /// The ID of a type variable. |
@@ -370,7 +373,7 @@ impl Ty { | |||
370 | } | 373 | } |
371 | 374 | ||
372 | // Resolve the path (in type namespace) | 375 | // Resolve the path (in type namespace) |
373 | let resolution = resolver.resolve_path(db, path).take_types(); | 376 | let resolution = resolver.resolve_path(db, path).into_per_ns().take_types(); |
374 | 377 | ||
375 | let def = match resolution { | 378 | let def = match resolution { |
376 | Some(Resolution::Def(def)) => def, | 379 | Some(Resolution::Def(def)) => def, |
@@ -678,6 +681,19 @@ fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty { | |||
678 | Ty::FnDef { def: def.into(), sig, name, substs } | 681 | Ty::FnDef { def: def.into(), sig, name, substs } |
679 | } | 682 | } |
680 | 683 | ||
684 | fn type_for_const(db: &impl HirDatabase, resolver: &Resolver, def: Const) -> Ty { | ||
685 | let node = def.source(db).1; | ||
686 | |||
687 | let tr = node | ||
688 | .type_ref() | ||
689 | .map(TypeRef::from_ast) | ||
690 | .as_ref() | ||
691 | .map(|tr| Ty::from_hir(db, resolver, tr)) | ||
692 | .unwrap_or_else(|| Ty::Unknown); | ||
693 | |||
694 | tr | ||
695 | } | ||
696 | |||
681 | /// Compute the type of a tuple struct constructor. | 697 | /// Compute the type of a tuple struct constructor. |
682 | fn type_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> Ty { | 698 | fn type_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> Ty { |
683 | let var_data = def.variant_data(db); | 699 | let var_data = def.variant_data(db); |
@@ -1172,15 +1188,56 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1172 | } | 1188 | } |
1173 | 1189 | ||
1174 | fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> { | 1190 | fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> { |
1175 | let resolved = resolver.resolve_path(self.db, &path).take_values()?; | 1191 | let resolved = resolver.resolve_path(self.db, &path); |
1192 | |||
1193 | let (resolved, segment_index) = match resolved { | ||
1194 | PathResult::FullyResolved(def) => (def.take_values()?, None), | ||
1195 | PathResult::PartiallyResolved(def, index) => (def.take_types()?, Some(index)), | ||
1196 | }; | ||
1197 | |||
1176 | match resolved { | 1198 | match resolved { |
1177 | Resolution::Def(def) => { | 1199 | Resolution::Def(def) => { |
1178 | let typable: Option<TypableDef> = def.into(); | 1200 | let typable: Option<TypableDef> = def.into(); |
1179 | let typable = typable?; | 1201 | let typable = typable?; |
1180 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | 1202 | |
1181 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); | 1203 | if let Some(segment_index) = segment_index { |
1182 | let ty = self.insert_type_vars(ty); | 1204 | let ty = self.db.type_for_def(typable, Namespace::Types); |
1183 | Some(ty) | 1205 | // TODO: What to do if segment_index is not the last segment |
1206 | // in the path | ||
1207 | let segment = &path.segments[segment_index]; | ||
1208 | |||
1209 | // Attempt to find an impl_item for the type which has a name matching | ||
1210 | // the current segment | ||
1211 | let ty = ty.iterate_impl_items(self.db, |item| match item { | ||
1212 | crate::ImplItem::Method(func) => { | ||
1213 | let sig = func.signature(self.db); | ||
1214 | if segment.name == *sig.name() { | ||
1215 | return Some(type_for_fn(self.db, func)); | ||
1216 | } | ||
1217 | None | ||
1218 | } | ||
1219 | crate::ImplItem::Const(c) => { | ||
1220 | let node = c.source(self.db).1; | ||
1221 | |||
1222 | if let Some(name) = node.name().map(|n| n.as_name()) { | ||
1223 | if segment.name == name { | ||
1224 | return Some(type_for_const(self.db, resolver, c)); | ||
1225 | } | ||
1226 | } | ||
1227 | |||
1228 | None | ||
1229 | } | ||
1230 | |||
1231 | // TODO: Resolve associated types | ||
1232 | crate::ImplItem::Type(_) => None, | ||
1233 | }); | ||
1234 | ty | ||
1235 | } else { | ||
1236 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | ||
1237 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); | ||
1238 | let ty = self.insert_type_vars(ty); | ||
1239 | Some(ty) | ||
1240 | } | ||
1184 | } | 1241 | } |
1185 | Resolution::LocalBinding(pat) => { | 1242 | Resolution::LocalBinding(pat) => { |
1186 | let ty = self.type_of_pat.get(pat)?; | 1243 | let ty = self.type_of_pat.get(pat)?; |
@@ -1204,23 +1261,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1204 | None => return (Ty::Unknown, None), | 1261 | None => return (Ty::Unknown, None), |
1205 | }; | 1262 | }; |
1206 | let resolver = &self.resolver; | 1263 | let resolver = &self.resolver; |
1207 | let typable: Option<TypableDef> = match resolver.resolve_path(self.db, &path).take_types() { | 1264 | let typable: Option<TypableDef> = |
1208 | Some(Resolution::Def(def)) => def.into(), | 1265 | match resolver.resolve_path(self.db, &path).into_per_ns().take_types() { |
1209 | Some(Resolution::LocalBinding(..)) => { | 1266 | Some(Resolution::Def(def)) => def.into(), |
1210 | // this cannot happen | 1267 | Some(Resolution::LocalBinding(..)) => { |
1211 | log::error!("path resolved to local binding in type ns"); | 1268 | // this cannot happen |
1212 | return (Ty::Unknown, None); | 1269 | log::error!("path resolved to local binding in type ns"); |
1213 | } | 1270 | return (Ty::Unknown, None); |
1214 | Some(Resolution::GenericParam(..)) => { | 1271 | } |
1215 | // generic params can't be used in struct literals | 1272 | Some(Resolution::GenericParam(..)) => { |
1216 | return (Ty::Unknown, None); | 1273 | // generic params can't be used in struct literals |
1217 | } | 1274 | return (Ty::Unknown, None); |
1218 | Some(Resolution::SelfType(..)) => { | 1275 | } |
1219 | // TODO this is allowed in an impl for a struct, handle this | 1276 | Some(Resolution::SelfType(..)) => { |
1220 | return (Ty::Unknown, None); | 1277 | // TODO this is allowed in an impl for a struct, handle this |
1221 | } | 1278 | return (Ty::Unknown, None); |
1222 | None => return (Ty::Unknown, None), | 1279 | } |
1223 | }; | 1280 | None => return (Ty::Unknown, None), |
1281 | }; | ||
1224 | let def = match typable { | 1282 | let def = match typable { |
1225 | None => return (Ty::Unknown, None), | 1283 | None => return (Ty::Unknown, None), |
1226 | Some(it) => it, | 1284 | Some(it) => it, |
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap new file mode 100644 index 000000000..14ab8ba96 --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap | |||
@@ -0,0 +1,14 @@ | |||
1 | --- | ||
2 | created: "2019-02-20T11:04:56.553382800Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_hir/src/ty/tests.rs | ||
5 | expression: "&result" | ||
6 | --- | ||
7 | [227; 305) '{ ...:ID; }': () | ||
8 | [237; 238) 'x': u32 | ||
9 | [241; 252) 'Struct::FOO': u32 | ||
10 | [262; 263) 'y': u32 | ||
11 | [266; 275) 'Enum::BAR': u32 | ||
12 | [285; 286) 'z': u32 | ||
13 | [289; 302) 'TraitTest::ID': u32 | ||
14 | |||
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_enum.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_enum.snap new file mode 100644 index 000000000..8f27af88c --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_enum.snap | |||
@@ -0,0 +1,20 @@ | |||
1 | --- | ||
2 | created: "2019-02-20T11:04:56.553382800Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_hir/src/ty/tests.rs | ||
5 | expression: "&result" | ||
6 | --- | ||
7 | [48; 68) '{ ... }': A | ||
8 | [58; 62) 'A::B': A | ||
9 | [89; 109) '{ ... }': A | ||
10 | [99; 103) 'A::C': A | ||
11 | [122; 179) '{ ... c; }': () | ||
12 | [132; 133) 'a': A | ||
13 | [136; 140) 'A::b': fn b() -> A | ||
14 | [136; 142) 'A::b()': A | ||
15 | [148; 149) 'a': A | ||
16 | [159; 160) 'c': A | ||
17 | [163; 167) 'A::c': fn c() -> A | ||
18 | [163; 169) 'A::c()': A | ||
19 | [175; 176) 'c': A | ||
20 | |||
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap new file mode 100644 index 000000000..fe5d6590e --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap | |||
@@ -0,0 +1,16 @@ | |||
1 | --- | ||
2 | created: "2019-02-21T10:25:18.568887300Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_hir/src/ty/tests.rs | ||
5 | expression: "&result" | ||
6 | --- | ||
7 | [64; 67) 'val': T | ||
8 | [82; 109) '{ ... }': Gen<T> | ||
9 | [92; 103) 'Gen { val }': Gen<T> | ||
10 | [98; 101) 'val': T | ||
11 | [123; 155) '{ ...32); }': () | ||
12 | [133; 134) 'a': Gen<[unknown]> | ||
13 | [137; 146) 'Gen::make': fn make<[unknown]>(T) -> Gen<T> | ||
14 | [137; 152) 'Gen::make(0u32)': Gen<[unknown]> | ||
15 | [147; 151) '0u32': u32 | ||
16 | |||
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_struct.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_struct.snap new file mode 100644 index 000000000..29ca9b0ea --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_struct.snap | |||
@@ -0,0 +1,16 @@ | |||
1 | --- | ||
2 | created: "2019-02-20T11:04:56.553382800Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_hir/src/ty/tests.rs | ||
5 | expression: "&result" | ||
6 | --- | ||
7 | [50; 76) '{ ... }': A | ||
8 | [60; 70) 'A { x: 0 }': A | ||
9 | [67; 68) '0': u32 | ||
10 | [89; 123) '{ ...a.x; }': () | ||
11 | [99; 100) 'a': A | ||
12 | [103; 109) 'A::new': fn new() -> A | ||
13 | [103; 111) 'A::new()': A | ||
14 | [117; 118) 'a': A | ||
15 | [117; 120) 'a.x': u32 | ||
16 | |||
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_with_modules.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_with_modules.snap new file mode 100644 index 000000000..6584bef39 --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_with_modules.snap | |||
@@ -0,0 +1,23 @@ | |||
1 | --- | ||
2 | created: "2019-02-21T08:55:53.926725400Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_hir/src/ty/tests.rs | ||
5 | expression: "&result" | ||
6 | --- | ||
7 | [56; 64) '{ A {} }': A | ||
8 | [58; 62) 'A {}': A | ||
9 | [126; 132) '{ 99 }': u32 | ||
10 | [128; 130) '99': u32 | ||
11 | [202; 210) '{ C {} }': C | ||
12 | [204; 208) 'C {}': C | ||
13 | [241; 325) '{ ...g(); }': () | ||
14 | [251; 252) 'x': A | ||
15 | [255; 266) 'a::A::thing': fn thing() -> A | ||
16 | [255; 268) 'a::A::thing()': A | ||
17 | [278; 279) 'y': u32 | ||
18 | [282; 293) 'b::B::thing': fn thing() -> u32 | ||
19 | [282; 295) 'b::B::thing()': u32 | ||
20 | [305; 306) 'z': C | ||
21 | [309; 320) 'c::C::thing': fn thing() -> C | ||
22 | [309; 322) 'c::C::thing()': C | ||
23 | |||
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 4ab442b8a..c4b452ba4 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -587,6 +587,139 @@ fn test() -> i128 { | |||
587 | } | 587 | } |
588 | 588 | ||
589 | #[test] | 589 | #[test] |
590 | fn infer_associated_const() { | ||
591 | check_inference( | ||
592 | "infer_associated_const", | ||
593 | r#" | ||
594 | struct Struct; | ||
595 | |||
596 | impl Struct { | ||
597 | const FOO: u32 = 1; | ||
598 | } | ||
599 | |||
600 | enum Enum; | ||
601 | |||
602 | impl Enum { | ||
603 | const BAR: u32 = 2; | ||
604 | } | ||
605 | |||
606 | trait Trait { | ||
607 | const ID: u32; | ||
608 | } | ||
609 | |||
610 | struct TraitTest; | ||
611 | |||
612 | impl Trait for TraitTest { | ||
613 | const ID: u32 = 5; | ||
614 | } | ||
615 | |||
616 | fn test() { | ||
617 | let x = Struct::FOO; | ||
618 | let y = Enum::BAR; | ||
619 | let z = TraitTest::ID; | ||
620 | } | ||
621 | "#, | ||
622 | ); | ||
623 | } | ||
624 | |||
625 | #[test] | ||
626 | fn infer_associated_method_struct() { | ||
627 | check_inference( | ||
628 | "infer_associated_method_struct", | ||
629 | r#" | ||
630 | struct A { x: u32 }; | ||
631 | |||
632 | impl A { | ||
633 | fn new() -> A { | ||
634 | A { x: 0 } | ||
635 | } | ||
636 | } | ||
637 | fn test() { | ||
638 | let a = A::new(); | ||
639 | a.x; | ||
640 | } | ||
641 | "#, | ||
642 | ); | ||
643 | } | ||
644 | |||
645 | #[test] | ||
646 | fn infer_associated_method_enum() { | ||
647 | check_inference( | ||
648 | "infer_associated_method_enum", | ||
649 | r#" | ||
650 | enum A { B, C }; | ||
651 | |||
652 | impl A { | ||
653 | pub fn b() -> A { | ||
654 | A::B | ||
655 | } | ||
656 | pub fn c() -> A { | ||
657 | A::C | ||
658 | } | ||
659 | } | ||
660 | fn test() { | ||
661 | let a = A::b(); | ||
662 | a; | ||
663 | let c = A::c(); | ||
664 | c; | ||
665 | } | ||
666 | "#, | ||
667 | ); | ||
668 | } | ||
669 | |||
670 | #[test] | ||
671 | fn infer_associated_method_with_modules() { | ||
672 | check_inference( | ||
673 | "infer_associated_method_with_modules", | ||
674 | r#" | ||
675 | mod a { | ||
676 | struct A; | ||
677 | impl A { pub fn thing() -> A { A {} }} | ||
678 | } | ||
679 | |||
680 | mod b { | ||
681 | struct B; | ||
682 | impl B { pub fn thing() -> u32 { 99 }} | ||
683 | |||
684 | mod c { | ||
685 | struct C; | ||
686 | impl C { pub fn thing() -> C { C {} }} | ||
687 | } | ||
688 | } | ||
689 | use b::c; | ||
690 | |||
691 | fn test() { | ||
692 | let x = a::A::thing(); | ||
693 | let y = b::B::thing(); | ||
694 | let z = c::C::thing(); | ||
695 | } | ||
696 | "#, | ||
697 | ); | ||
698 | } | ||
699 | |||
700 | #[test] | ||
701 | fn infer_associated_method_generics() { | ||
702 | check_inference( | ||
703 | "infer_associated_method_generics", | ||
704 | r#" | ||
705 | struct Gen<T> { | ||
706 | val: T | ||
707 | } | ||
708 | |||
709 | impl<T> Gen<T> { | ||
710 | pub fn make(val: T) -> Gen<T> { | ||
711 | Gen { val } | ||
712 | } | ||
713 | } | ||
714 | |||
715 | fn test() { | ||
716 | let a = Gen::make(0u32); | ||
717 | } | ||
718 | "#, | ||
719 | ); | ||
720 | } | ||
721 | |||
722 | #[test] | ||
590 | fn no_panic_on_field_of_enum() { | 723 | fn no_panic_on_field_of_enum() { |
591 | check_inference( | 724 | check_inference( |
592 | "no_panic_on_field_of_enum", | 725 | "no_panic_on_field_of_enum", |
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index d337fe970..a0c5572d5 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs | |||
@@ -10,7 +10,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
10 | Some(path) => path.clone(), | 10 | Some(path) => path.clone(), |
11 | _ => return, | 11 | _ => return, |
12 | }; | 12 | }; |
13 | let def = match ctx.resolver.resolve_path(ctx.db, &path).take_types() { | 13 | let def = match ctx.resolver.resolve_path(ctx.db, &path).into_per_ns().take_types() { |
14 | Some(Resolution::Def(def)) => def, | 14 | Some(Resolution::Def(def)) => def, |
15 | _ => return, | 15 | _ => return, |
16 | }; | 16 | }; |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 96ed8c8e9..76aaebd52 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -79,7 +79,7 @@ pub(crate) fn reference_definition( | |||
79 | if let Some(path) = | 79 | if let Some(path) = |
80 | name_ref.syntax().ancestors().find_map(ast::Path::cast).and_then(hir::Path::from_ast) | 80 | name_ref.syntax().ancestors().find_map(ast::Path::cast).and_then(hir::Path::from_ast) |
81 | { | 81 | { |
82 | let resolved = resolver.resolve_path(db, &path); | 82 | let resolved = resolver.resolve_path(db, &path).into_per_ns(); |
83 | match resolved.clone().take_types().or_else(|| resolved.take_values()) { | 83 | match resolved.clone().take_types().or_else(|| resolved.take_values()) { |
84 | Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), | 84 | Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), |
85 | Some(Resolution::LocalBinding(pat)) => { | 85 | Some(Resolution::LocalBinding(pat)) => { |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 0888ab6de..38671b394 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -223,4 +223,24 @@ mod tests { | |||
223 | assert_eq!("usize", &type_name); | 223 | assert_eq!("usize", &type_name); |
224 | } | 224 | } |
225 | 225 | ||
226 | #[test] | ||
227 | fn test_hover_infer_associated_method_result() { | ||
228 | let (analysis, position) = single_file_with_position( | ||
229 | " | ||
230 | struct Thing { x: u32 }; | ||
231 | |||
232 | impl Thing { | ||
233 | fn new() -> Thing { | ||
234 | Thing { x: 0 } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | fn main() { | ||
239 | let foo_<|>test = Thing::new(); | ||
240 | } | ||
241 | ", | ||
242 | ); | ||
243 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
244 | assert_eq!(hover.info, "Thing"); | ||
245 | } | ||
226 | } | 246 | } |