aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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}