diff options
-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 | } |