diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-22 19:58:22 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-22 19:58:22 +0000 |
commit | 3d8a0982a12f3aa4b8c193a841f864b15c3cb66e (patch) | |
tree | e20ae00628cc28417e22621f583ba6988677ffcd | |
parent | bb665a70627cbc2f4fb930fefb04899941b6afa6 (diff) | |
parent | 247d1c17b385ff8a8c1dda2e899495146b643b98 (diff) |
Merge #866
866: Implement basic support for Associated Methods r=flodiebold a=vipentti
This is my attempt at learning to understand how the type inference works by adding basic support for associated methods. Currently it does not resolve associated types or constants.
The basic idea is that `Resolver::resolve_path` returns a new `PathResult` type, which has two variants, `FullyResolved` and `PartiallyResolved`, fully resolved matches the previous behavior, where as `PartiallyResolved` contains the `PerNs<Resolution` in addition to a `segment_index` which contains the index of the segment which we failed to resolve. This index can then be used to continue inference in `infer_path_expr` using the `Type` we managed to resolve.
This changes some of the previous apis, so looking for feedback and suggestions.
This should enable fixing #832
Co-authored-by: Ville Penttinen <[email protected]>
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 77 | ||||
-rw-r--r-- | crates/ra_hir/src/resolve.rs | 89 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 52 | ||||
-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 | 134 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 20 |
10 files changed, 430 insertions, 31 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index bd920bfea..8067b8415 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -297,7 +297,14 @@ where | |||
297 | ); | 297 | ); |
298 | (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes }) | 298 | (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes }) |
299 | } else { | 299 | } else { |
300 | self.result.resolve_path_fp(self.db, ResolveMode::Import, original_module, &import.path) | 300 | let res = self.result.resolve_path_fp( |
301 | self.db, | ||
302 | ResolveMode::Import, | ||
303 | original_module, | ||
304 | &import.path, | ||
305 | ); | ||
306 | |||
307 | (res.resolved_def, res.reached_fixedpoint) | ||
301 | }; | 308 | }; |
302 | 309 | ||
303 | if reached_fixedpoint != ReachedFixedPoint::Yes { | 310 | if reached_fixedpoint != ReachedFixedPoint::Yes { |
@@ -435,6 +442,27 @@ where | |||
435 | } | 442 | } |
436 | } | 443 | } |
437 | 444 | ||
445 | #[derive(Debug, Clone)] | ||
446 | struct ResolvePathResult { | ||
447 | resolved_def: PerNs<ModuleDef>, | ||
448 | segment_index: Option<usize>, | ||
449 | reached_fixedpoint: ReachedFixedPoint, | ||
450 | } | ||
451 | |||
452 | impl ResolvePathResult { | ||
453 | fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { | ||
454 | ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None) | ||
455 | } | ||
456 | |||
457 | fn with( | ||
458 | resolved_def: PerNs<ModuleDef>, | ||
459 | reached_fixedpoint: ReachedFixedPoint, | ||
460 | segment_index: Option<usize>, | ||
461 | ) -> ResolvePathResult { | ||
462 | ResolvePathResult { resolved_def, reached_fixedpoint, segment_index } | ||
463 | } | ||
464 | } | ||
465 | |||
438 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 466 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
439 | enum ResolveMode { | 467 | enum ResolveMode { |
440 | Import, | 468 | Import, |
@@ -468,8 +496,9 @@ impl ItemMap { | |||
468 | db: &impl PersistentHirDatabase, | 496 | db: &impl PersistentHirDatabase, |
469 | original_module: Module, | 497 | original_module: Module, |
470 | path: &Path, | 498 | path: &Path, |
471 | ) -> PerNs<ModuleDef> { | 499 | ) -> (PerNs<ModuleDef>, Option<usize>) { |
472 | self.resolve_path_fp(db, ResolveMode::Other, original_module, path).0 | 500 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); |
501 | (res.resolved_def, res.segment_index) | ||
473 | } | 502 | } |
474 | 503 | ||
475 | fn resolve_in_prelude( | 504 | fn resolve_in_prelude( |
@@ -534,7 +563,7 @@ impl ItemMap { | |||
534 | mode: ResolveMode, | 563 | mode: ResolveMode, |
535 | original_module: Module, | 564 | original_module: Module, |
536 | path: &Path, | 565 | path: &Path, |
537 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | 566 | ) -> ResolvePathResult { |
538 | let mut segments = path.segments.iter().enumerate(); | 567 | let mut segments = path.segments.iter().enumerate(); |
539 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { | 568 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { |
540 | PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), | 569 | PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), |
@@ -549,7 +578,7 @@ impl ItemMap { | |||
549 | { | 578 | { |
550 | let segment = match segments.next() { | 579 | let segment = match segments.next() { |
551 | Some((_, segment)) => segment, | 580 | Some((_, segment)) => segment, |
552 | None => return (PerNs::none(), ReachedFixedPoint::Yes), | 581 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
553 | }; | 582 | }; |
554 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | 583 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); |
555 | self.resolve_name_in_crate_root_or_extern_prelude( | 584 | self.resolve_name_in_crate_root_or_extern_prelude( |
@@ -561,7 +590,7 @@ impl ItemMap { | |||
561 | PathKind::Plain => { | 590 | PathKind::Plain => { |
562 | let segment = match segments.next() { | 591 | let segment = match segments.next() { |
563 | Some((_, segment)) => segment, | 592 | Some((_, segment)) => segment, |
564 | None => return (PerNs::none(), ReachedFixedPoint::Yes), | 593 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
565 | }; | 594 | }; |
566 | log::debug!("resolving {:?} in module", segment); | 595 | log::debug!("resolving {:?} in module", segment); |
567 | self.resolve_name_in_module(db, original_module, &segment.name) | 596 | self.resolve_name_in_module(db, original_module, &segment.name) |
@@ -571,20 +600,20 @@ impl ItemMap { | |||
571 | PerNs::types(p.into()) | 600 | PerNs::types(p.into()) |
572 | } else { | 601 | } else { |
573 | log::debug!("super path in root module"); | 602 | log::debug!("super path in root module"); |
574 | return (PerNs::none(), ReachedFixedPoint::Yes); | 603 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); |
575 | } | 604 | } |
576 | } | 605 | } |
577 | PathKind::Abs => { | 606 | PathKind::Abs => { |
578 | // 2018-style absolute path -- only extern prelude | 607 | // 2018-style absolute path -- only extern prelude |
579 | let segment = match segments.next() { | 608 | let segment = match segments.next() { |
580 | Some((_, segment)) => segment, | 609 | Some((_, segment)) => segment, |
581 | None => return (PerNs::none(), ReachedFixedPoint::Yes), | 610 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
582 | }; | 611 | }; |
583 | if let Some(def) = self.extern_prelude.get(&segment.name) { | 612 | if let Some(def) = self.extern_prelude.get(&segment.name) { |
584 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); | 613 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); |
585 | PerNs::types(*def) | 614 | PerNs::types(*def) |
586 | } else { | 615 | } else { |
587 | return (PerNs::none(), ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude | 616 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude |
588 | } | 617 | } |
589 | } | 618 | } |
590 | }; | 619 | }; |
@@ -598,7 +627,7 @@ impl ItemMap { | |||
598 | // (don't break here because `curr_per_ns` might contain | 627 | // (don't break here because `curr_per_ns` might contain |
599 | // something in the value namespace, and it would be wrong | 628 | // something in the value namespace, and it would be wrong |
600 | // to return that) | 629 | // to return that) |
601 | return (PerNs::none(), ReachedFixedPoint::No); | 630 | return ResolvePathResult::empty(ReachedFixedPoint::No); |
602 | } | 631 | } |
603 | }; | 632 | }; |
604 | // resolve segment in curr | 633 | // resolve segment in curr |
@@ -612,15 +641,15 @@ impl ItemMap { | |||
612 | }; | 641 | }; |
613 | log::debug!("resolving {:?} in other crate", path); | 642 | log::debug!("resolving {:?} in other crate", path); |
614 | let item_map = db.item_map(module.krate); | 643 | let item_map = db.item_map(module.krate); |
615 | let def = item_map.resolve_path(db, *module, &path); | 644 | let (def, s) = item_map.resolve_path(db, *module, &path); |
616 | return (def, ReachedFixedPoint::Yes); | 645 | return ResolvePathResult::with(def, ReachedFixedPoint::Yes, s); |
617 | } | 646 | } |
618 | 647 | ||
619 | match self[module.module_id].items.get(&segment.name) { | 648 | match self[module.module_id].items.get(&segment.name) { |
620 | Some(res) if !res.def.is_none() => res.def, | 649 | Some(res) if !res.def.is_none() => res.def, |
621 | _ => { | 650 | _ => { |
622 | log::debug!("path segment {:?} not found", segment.name); | 651 | log::debug!("path segment {:?} not found", segment.name); |
623 | return (PerNs::none(), ReachedFixedPoint::No); | 652 | return ResolvePathResult::empty(ReachedFixedPoint::No); |
624 | } | 653 | } |
625 | } | 654 | } |
626 | } | 655 | } |
@@ -629,23 +658,33 @@ impl ItemMap { | |||
629 | tested_by!(item_map_enum_importing); | 658 | tested_by!(item_map_enum_importing); |
630 | match e.variant(db, &segment.name) { | 659 | match e.variant(db, &segment.name) { |
631 | Some(variant) => PerNs::both(variant.into(), variant.into()), | 660 | Some(variant) => PerNs::both(variant.into(), variant.into()), |
632 | None => PerNs::none(), | 661 | None => { |
662 | return ResolvePathResult::with( | ||
663 | PerNs::types((*e).into()), | ||
664 | ReachedFixedPoint::Yes, | ||
665 | Some(i), | ||
666 | ); | ||
667 | } | ||
633 | } | 668 | } |
634 | } | 669 | } |
635 | _ => { | 670 | s => { |
636 | // could be an inherent method call in UFCS form | 671 | // could be an inherent method call in UFCS form |
637 | // (`Struct::method`), or some other kind of associated | 672 | // (`Struct::method`), or some other kind of associated item |
638 | // item... Which we currently don't handle (TODO) | ||
639 | log::debug!( | 673 | log::debug!( |
640 | "path segment {:?} resolved to non-module {:?}, but is not last", | 674 | "path segment {:?} resolved to non-module {:?}, but is not last", |
641 | segment.name, | 675 | segment.name, |
642 | curr, | 676 | curr, |
643 | ); | 677 | ); |
644 | return (PerNs::none(), ReachedFixedPoint::Yes); | 678 | |
679 | return ResolvePathResult::with( | ||
680 | PerNs::types((*s).into()), | ||
681 | ReachedFixedPoint::Yes, | ||
682 | Some(i), | ||
683 | ); | ||
645 | } | 684 | } |
646 | }; | 685 | }; |
647 | } | 686 | } |
648 | (curr_per_ns, ReachedFixedPoint::Yes) | 687 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) |
649 | } | 688 | } |
650 | } | 689 | } |
651 | 690 | ||
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 91a531801..57e7d0b9a 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs | |||
@@ -33,6 +33,67 @@ pub(crate) struct ExprScope { | |||
33 | } | 33 | } |
34 | 34 | ||
35 | #[derive(Debug, Clone)] | 35 | #[derive(Debug, Clone)] |
36 | pub(crate) struct PathResult { | ||
37 | /// The actual path resolution | ||
38 | resolution: PerNs<Resolution>, | ||
39 | /// The first index in the path that we | ||
40 | /// were unable to resolve. | ||
41 | /// When path is fully resolved, this is 0. | ||
42 | remaining_index: usize, | ||
43 | } | ||
44 | |||
45 | impl PathResult { | ||
46 | /// Returns the remaining index in the result | ||
47 | /// returns None if the path was fully resolved | ||
48 | pub(crate) fn remaining_index(&self) -> Option<usize> { | ||
49 | if self.remaining_index > 0 { | ||
50 | Some(self.remaining_index) | ||
51 | } else { | ||
52 | None | ||
53 | } | ||
54 | } | ||
55 | |||
56 | /// Consumes `PathResult` and returns the contained `PerNs<Resolution>` | ||
57 | /// if the path was fully resolved, meaning we have no remaining items | ||
58 | pub(crate) fn into_fully_resolved(self) -> PerNs<Resolution> { | ||
59 | if self.is_fully_resolved() { | ||
60 | self.resolution | ||
61 | } else { | ||
62 | PerNs::none() | ||
63 | } | ||
64 | } | ||
65 | |||
66 | /// Consumes `PathResult` and returns the resolution and the | ||
67 | /// remaining_index as a tuple. | ||
68 | pub(crate) fn into_inner(self) -> (PerNs<Resolution>, Option<usize>) { | ||
69 | let index = self.remaining_index(); | ||
70 | (self.resolution, index) | ||
71 | } | ||
72 | |||
73 | /// Path is fully resolved when `remaining_index` is none | ||
74 | /// and the resolution contains anything | ||
75 | pub(crate) fn is_fully_resolved(&self) -> bool { | ||
76 | !self.resolution.is_none() && self.remaining_index().is_none() | ||
77 | } | ||
78 | |||
79 | fn empty() -> PathResult { | ||
80 | PathResult { resolution: PerNs::none(), remaining_index: 0 } | ||
81 | } | ||
82 | |||
83 | fn from_resolution(res: PerNs<Resolution>) -> PathResult { | ||
84 | PathResult::from_resolution_with_index(res, 0) | ||
85 | } | ||
86 | |||
87 | fn from_resolution_with_index(res: PerNs<Resolution>, remaining_index: usize) -> PathResult { | ||
88 | if res.is_none() { | ||
89 | PathResult::empty() | ||
90 | } else { | ||
91 | PathResult { resolution: res, remaining_index } | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | |||
96 | #[derive(Debug, Clone)] | ||
36 | pub(crate) enum Scope { | 97 | pub(crate) enum Scope { |
37 | /// All the items and imported names of a module | 98 | /// All the items and imported names of a module |
38 | ModuleScope(ModuleItemMap), | 99 | ModuleScope(ModuleItemMap), |
@@ -67,21 +128,37 @@ impl Resolver { | |||
67 | resolution | 128 | resolution |
68 | } | 129 | } |
69 | 130 | ||
70 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> { | 131 | /// Returns the resolved path segments |
132 | /// Which may be fully resolved, empty or partially resolved. | ||
133 | pub(crate) fn resolve_path_segments(&self, db: &impl HirDatabase, path: &Path) -> PathResult { | ||
71 | if let Some(name) = path.as_ident() { | 134 | if let Some(name) = path.as_ident() { |
72 | self.resolve_name(db, name) | 135 | PathResult::from_resolution(self.resolve_name(db, name)) |
73 | } else if path.is_self() { | 136 | } else if path.is_self() { |
74 | self.resolve_name(db, &Name::self_param()) | 137 | PathResult::from_resolution(self.resolve_name(db, &Name::self_param())) |
75 | } else { | 138 | } else { |
76 | let (item_map, module) = match self.module() { | 139 | let (item_map, module) = match self.module() { |
77 | Some(m) => m, | 140 | Some(m) => m, |
78 | _ => return PerNs::none(), | 141 | _ => return PathResult::empty(), |
79 | }; | 142 | }; |
80 | let module_res = item_map.resolve_path(db, module, path); | 143 | let (module_res, segment_index) = item_map.resolve_path(db, module, path); |
81 | module_res.map(Resolution::Def) | 144 | |
145 | let def = module_res.map(Resolution::Def); | ||
146 | |||
147 | if let Some(index) = segment_index { | ||
148 | PathResult::from_resolution_with_index(def, index) | ||
149 | } else { | ||
150 | PathResult::from_resolution(def) | ||
151 | } | ||
82 | } | 152 | } |
83 | } | 153 | } |
84 | 154 | ||
155 | /// Returns the fully resolved path if we were able to resolve it. | ||
156 | /// otherwise returns `PerNs::none` | ||
157 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> { | ||
158 | // into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise | ||
159 | self.resolve_path_segments(db, path).into_fully_resolved() | ||
160 | } | ||
161 | |||
85 | pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> { | 162 | pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> { |
86 | let mut names = FxHashMap::default(); | 163 | let mut names = FxHashMap::default(); |
87 | for scope in self.scopes.iter().rev() { | 164 | for scope in self.scopes.iter().rev() { |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 1a3e1994f..ae595c16d 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -37,7 +37,7 @@ use crate::{ | |||
37 | FnSignature, ModuleDef, AdtDef, | 37 | FnSignature, ModuleDef, AdtDef, |
38 | HirDatabase, | 38 | HirDatabase, |
39 | type_ref::{TypeRef, Mutability}, | 39 | type_ref::{TypeRef, Mutability}, |
40 | name::KnownName, | 40 | name::{KnownName}, |
41 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, | 41 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, |
42 | generics::GenericParams, | 42 | generics::GenericParams, |
43 | path::{ GenericArgs, GenericArg}, | 43 | path::{ GenericArgs, GenericArg}, |
@@ -1166,15 +1166,55 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1166 | } | 1166 | } |
1167 | 1167 | ||
1168 | fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> { | 1168 | fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> { |
1169 | let resolved = resolver.resolve_path(self.db, &path).take_values()?; | 1169 | let resolved = resolver.resolve_path_segments(self.db, &path); |
1170 | |||
1171 | let (def, remaining_index) = resolved.into_inner(); | ||
1172 | |||
1173 | // if the remaining_index is None, we expect the path | ||
1174 | // to be fully resolved, in this case we continue with | ||
1175 | // the default by attempting to `take_values´ from the resolution. | ||
1176 | // Otherwise the path was partially resolved, which means | ||
1177 | // we might have resolved into a type for which | ||
1178 | // we may find some associated item starting at the | ||
1179 | // path.segment pointed to by `remaining_index´ | ||
1180 | let resolved = | ||
1181 | if remaining_index.is_none() { def.take_values()? } else { def.take_types()? }; | ||
1182 | |||
1170 | match resolved { | 1183 | match resolved { |
1171 | Resolution::Def(def) => { | 1184 | Resolution::Def(def) => { |
1172 | let typable: Option<TypableDef> = def.into(); | 1185 | let typable: Option<TypableDef> = def.into(); |
1173 | let typable = typable?; | 1186 | let typable = typable?; |
1174 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | 1187 | |
1175 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); | 1188 | if let Some(remaining_index) = remaining_index { |
1176 | let ty = self.insert_type_vars(ty); | 1189 | let ty = self.db.type_for_def(typable, Namespace::Types); |
1177 | Some(ty) | 1190 | // TODO: Keep resolving the segments |
1191 | // if we have more segments to process | ||
1192 | let segment = &path.segments[remaining_index]; | ||
1193 | |||
1194 | // Attempt to find an impl_item for the type which has a name matching | ||
1195 | // the current segment | ||
1196 | let ty = ty.iterate_impl_items(self.db, |item| match item { | ||
1197 | crate::ImplItem::Method(func) => { | ||
1198 | let sig = func.signature(self.db); | ||
1199 | if segment.name == *sig.name() { | ||
1200 | return Some(type_for_fn(self.db, func)); | ||
1201 | } | ||
1202 | None | ||
1203 | } | ||
1204 | |||
1205 | // TODO: Resolve associated const | ||
1206 | crate::ImplItem::Const(_) => None, | ||
1207 | |||
1208 | // TODO: Resolve associated types | ||
1209 | crate::ImplItem::Type(_) => None, | ||
1210 | }); | ||
1211 | ty | ||
1212 | } else { | ||
1213 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | ||
1214 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); | ||
1215 | let ty = self.insert_type_vars(ty); | ||
1216 | Some(ty) | ||
1217 | } | ||
1178 | } | 1218 | } |
1179 | Resolution::LocalBinding(pat) => { | 1219 | Resolution::LocalBinding(pat) => { |
1180 | let ty = self.type_of_pat.get(pat)?; | 1220 | let ty = self.type_of_pat.get(pat)?; |
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..131d1fa16 --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap | |||
@@ -0,0 +1,14 @@ | |||
1 | --- | ||
2 | created: "2019-02-21T21:51:46.497925200Z" | ||
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': [unknown] | ||
9 | [241; 252) 'Struct::FOO': [unknown] | ||
10 | [262; 263) 'y': [unknown] | ||
11 | [266; 275) 'Enum::BAR': [unknown] | ||
12 | [285; 286) 'z': [unknown] | ||
13 | [289; 302) 'TraitTest::ID': [unknown] | ||
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 3affcb4fe..f04e9109c 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -608,6 +608,140 @@ fn test() -> i128 { | |||
608 | } | 608 | } |
609 | 609 | ||
610 | #[test] | 610 | #[test] |
611 | fn infer_associated_const() { | ||
612 | check_inference( | ||
613 | "infer_associated_const", | ||
614 | r#" | ||
615 | struct Struct; | ||
616 | |||
617 | impl Struct { | ||
618 | const FOO: u32 = 1; | ||
619 | } | ||
620 | |||
621 | enum Enum; | ||
622 | |||
623 | impl Enum { | ||
624 | const BAR: u32 = 2; | ||
625 | } | ||
626 | |||
627 | trait Trait { | ||
628 | const ID: u32; | ||
629 | } | ||
630 | |||
631 | struct TraitTest; | ||
632 | |||
633 | impl Trait for TraitTest { | ||
634 | const ID: u32 = 5; | ||
635 | } | ||
636 | |||
637 | fn test() { | ||
638 | let x = Struct::FOO; | ||
639 | let y = Enum::BAR; | ||
640 | let z = TraitTest::ID; | ||
641 | } | ||
642 | "#, | ||
643 | ); | ||
644 | } | ||
645 | |||
646 | #[test] | ||
647 | fn infer_associated_method_struct() { | ||
648 | check_inference( | ||
649 | "infer_associated_method_struct", | ||
650 | r#" | ||
651 | struct A { x: u32 }; | ||
652 | |||
653 | impl A { | ||
654 | fn new() -> A { | ||
655 | A { x: 0 } | ||
656 | } | ||
657 | } | ||
658 | fn test() { | ||
659 | let a = A::new(); | ||
660 | a.x; | ||
661 | } | ||
662 | "#, | ||
663 | ); | ||
664 | } | ||
665 | |||
666 | #[test] | ||
667 | fn infer_associated_method_enum() { | ||
668 | check_inference( | ||
669 | "infer_associated_method_enum", | ||
670 | r#" | ||
671 | enum A { B, C }; | ||
672 | |||
673 | impl A { | ||
674 | pub fn b() -> A { | ||
675 | A::B | ||
676 | } | ||
677 | pub fn c() -> A { | ||
678 | A::C | ||
679 | } | ||
680 | } | ||
681 | fn test() { | ||
682 | let a = A::b(); | ||
683 | a; | ||
684 | let c = A::c(); | ||
685 | c; | ||
686 | } | ||
687 | "#, | ||
688 | ); | ||
689 | } | ||
690 | |||
691 | #[test] | ||
692 | fn infer_associated_method_with_modules() { | ||
693 | check_inference( | ||
694 | "infer_associated_method_with_modules", | ||
695 | r#" | ||
696 | mod a { | ||
697 | struct A; | ||
698 | impl A { pub fn thing() -> A { A {} }} | ||
699 | } | ||
700 | |||
701 | mod b { | ||
702 | struct B; | ||
703 | impl B { pub fn thing() -> u32 { 99 }} | ||
704 | |||
705 | mod c { | ||
706 | struct C; | ||
707 | impl C { pub fn thing() -> C { C {} }} | ||
708 | } | ||
709 | } | ||
710 | use b::c; | ||
711 | |||
712 | fn test() { | ||
713 | let x = a::A::thing(); | ||
714 | let y = b::B::thing(); | ||
715 | let z = c::C::thing(); | ||
716 | } | ||
717 | "#, | ||
718 | ); | ||
719 | } | ||
720 | |||
721 | #[test] | ||
722 | #[ignore] // FIXME: After https://github.com/rust-analyzer/rust-analyzer/pull/866 is merged | ||
723 | fn infer_associated_method_generics() { | ||
724 | check_inference( | ||
725 | "infer_associated_method_generics", | ||
726 | r#" | ||
727 | struct Gen<T> { | ||
728 | val: T | ||
729 | } | ||
730 | |||
731 | impl<T> Gen<T> { | ||
732 | pub fn make(val: T) -> Gen<T> { | ||
733 | Gen { val } | ||
734 | } | ||
735 | } | ||
736 | |||
737 | fn test() { | ||
738 | let a = Gen::make(0u32); | ||
739 | } | ||
740 | "#, | ||
741 | ); | ||
742 | } | ||
743 | |||
744 | #[test] | ||
611 | fn no_panic_on_field_of_enum() { | 745 | fn no_panic_on_field_of_enum() { |
612 | check_inference( | 746 | check_inference( |
613 | "no_panic_on_field_of_enum", | 747 | "no_panic_on_field_of_enum", |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index a41c4e546..c62683ad4 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -240,4 +240,24 @@ mod tests { | |||
240 | assert_eq!("usize", &type_name); | 240 | assert_eq!("usize", &type_name); |
241 | } | 241 | } |
242 | 242 | ||
243 | #[test] | ||
244 | fn test_hover_infer_associated_method_result() { | ||
245 | let (analysis, position) = single_file_with_position( | ||
246 | " | ||
247 | struct Thing { x: u32 }; | ||
248 | |||
249 | impl Thing { | ||
250 | fn new() -> Thing { | ||
251 | Thing { x: 0 } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | fn main() { | ||
256 | let foo_<|>test = Thing::new(); | ||
257 | } | ||
258 | ", | ||
259 | ); | ||
260 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
261 | assert_eq!(hover.info, "Thing"); | ||
262 | } | ||
243 | } | 263 | } |