aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-02-22 19:58:22 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-02-22 19:58:22 +0000
commit3d8a0982a12f3aa4b8c193a841f864b15c3cb66e (patch)
treee20ae00628cc28417e22621f583ba6988677ffcd
parentbb665a70627cbc2f4fb930fefb04899941b6afa6 (diff)
parent247d1c17b385ff8a8c1dda2e899495146b643b98 (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.rs77
-rw-r--r--crates/ra_hir/src/resolve.rs89
-rw-r--r--crates/ra_hir/src/ty.rs52
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap14
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_enum.snap20
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap16
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_struct.snap16
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_with_modules.snap23
-rw-r--r--crates/ra_hir/src/ty/tests.rs134
-rw-r--r--crates/ra_ide_api/src/hover.rs20
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)]
446struct ResolvePathResult {
447 resolved_def: PerNs<ModuleDef>,
448 segment_index: Option<usize>,
449 reached_fixedpoint: ReachedFixedPoint,
450}
451
452impl 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)]
439enum ResolveMode { 467enum 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)]
36pub(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
45impl 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)]
36pub(crate) enum Scope { 97pub(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---
2created: "2019-02-21T21:51:46.497925200Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&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---
2created: "2019-02-20T11:04:56.553382800Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&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---
2created: "2019-02-21T10:25:18.568887300Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&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---
2created: "2019-02-20T11:04:56.553382800Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&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---
2created: "2019-02-21T08:55:53.926725400Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&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]
611fn infer_associated_const() {
612 check_inference(
613 "infer_associated_const",
614 r#"
615struct Struct;
616
617impl Struct {
618 const FOO: u32 = 1;
619}
620
621enum Enum;
622
623impl Enum {
624 const BAR: u32 = 2;
625}
626
627trait Trait {
628 const ID: u32;
629}
630
631struct TraitTest;
632
633impl Trait for TraitTest {
634 const ID: u32 = 5;
635}
636
637fn test() {
638 let x = Struct::FOO;
639 let y = Enum::BAR;
640 let z = TraitTest::ID;
641}
642"#,
643 );
644}
645
646#[test]
647fn infer_associated_method_struct() {
648 check_inference(
649 "infer_associated_method_struct",
650 r#"
651struct A { x: u32 };
652
653impl A {
654 fn new() -> A {
655 A { x: 0 }
656 }
657}
658fn test() {
659 let a = A::new();
660 a.x;
661}
662"#,
663 );
664}
665
666#[test]
667fn infer_associated_method_enum() {
668 check_inference(
669 "infer_associated_method_enum",
670 r#"
671enum A { B, C };
672
673impl A {
674 pub fn b() -> A {
675 A::B
676 }
677 pub fn c() -> A {
678 A::C
679 }
680}
681fn test() {
682 let a = A::b();
683 a;
684 let c = A::c();
685 c;
686}
687"#,
688 );
689}
690
691#[test]
692fn infer_associated_method_with_modules() {
693 check_inference(
694 "infer_associated_method_with_modules",
695 r#"
696mod a {
697 struct A;
698 impl A { pub fn thing() -> A { A {} }}
699}
700
701mod 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}
710use b::c;
711
712fn 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
723fn infer_associated_method_generics() {
724 check_inference(
725 "infer_associated_method_generics",
726 r#"
727struct Gen<T> {
728 val: T
729}
730
731impl<T> Gen<T> {
732 pub fn make(val: T) -> Gen<T> {
733 Gen { val }
734 }
735}
736
737fn test() {
738 let a = Gen::make(0u32);
739}
740"#,
741 );
742}
743
744#[test]
611fn no_panic_on_field_of_enum() { 745fn 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}