diff options
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 43 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 29 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 22 |
3 files changed, 87 insertions, 7 deletions
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 5e4d49ffb..f7a35f05b 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -54,6 +54,24 @@ pub fn infer(db: &impl HirDatabase, func: Function) -> Arc<InferenceResult> { | |||
54 | Arc::new(ctx.resolve_all()) | 54 | Arc::new(ctx.resolve_all()) |
55 | } | 55 | } |
56 | 56 | ||
57 | #[derive(Debug, Copy, Clone)] | ||
58 | enum ExprOrPatId { | ||
59 | Expr(ExprId), | ||
60 | Pat(PatId), | ||
61 | } | ||
62 | |||
63 | impl From<ExprId> for ExprOrPatId { | ||
64 | fn from(id: ExprId) -> Self { | ||
65 | ExprOrPatId::Expr(id) | ||
66 | } | ||
67 | } | ||
68 | |||
69 | impl From<PatId> for ExprOrPatId { | ||
70 | fn from(id: PatId) -> Self { | ||
71 | ExprOrPatId::Pat(id) | ||
72 | } | ||
73 | } | ||
74 | |||
57 | /// The result of type inference: A mapping from expressions and patterns to types. | 75 | /// The result of type inference: A mapping from expressions and patterns to types. |
58 | #[derive(Clone, PartialEq, Eq, Debug)] | 76 | #[derive(Clone, PartialEq, Eq, Debug)] |
59 | pub struct InferenceResult { | 77 | pub struct InferenceResult { |
@@ -61,6 +79,8 @@ pub struct InferenceResult { | |||
61 | method_resolutions: FxHashMap<ExprId, Function>, | 79 | method_resolutions: FxHashMap<ExprId, Function>, |
62 | /// For each field access expr, records the field it resolves to. | 80 | /// For each field access expr, records the field it resolves to. |
63 | field_resolutions: FxHashMap<ExprId, StructField>, | 81 | field_resolutions: FxHashMap<ExprId, StructField>, |
82 | /// For each associated function call expr, records the function it resolves to | ||
83 | assoc_fn_resolutions: FxHashMap<ExprId, Function>, | ||
64 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, | 84 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, |
65 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, | 85 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, |
66 | } | 86 | } |
@@ -72,6 +92,9 @@ impl InferenceResult { | |||
72 | pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> { | 92 | pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> { |
73 | self.field_resolutions.get(&expr).map(|it| *it) | 93 | self.field_resolutions.get(&expr).map(|it| *it) |
74 | } | 94 | } |
95 | pub fn assoc_fn_resolutions(&self, expr: ExprId) -> Option<Function> { | ||
96 | self.assoc_fn_resolutions.get(&expr).map(|it| *it) | ||
97 | } | ||
75 | } | 98 | } |
76 | 99 | ||
77 | impl Index<ExprId> for InferenceResult { | 100 | impl Index<ExprId> for InferenceResult { |
@@ -99,6 +122,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
99 | var_unification_table: InPlaceUnificationTable<TypeVarId>, | 122 | var_unification_table: InPlaceUnificationTable<TypeVarId>, |
100 | method_resolutions: FxHashMap<ExprId, Function>, | 123 | method_resolutions: FxHashMap<ExprId, Function>, |
101 | field_resolutions: FxHashMap<ExprId, StructField>, | 124 | field_resolutions: FxHashMap<ExprId, StructField>, |
125 | assoc_fn_resolutions: FxHashMap<ExprId, Function>, | ||
102 | type_of_expr: ArenaMap<ExprId, Ty>, | 126 | type_of_expr: ArenaMap<ExprId, Ty>, |
103 | type_of_pat: ArenaMap<PatId, Ty>, | 127 | type_of_pat: ArenaMap<PatId, Ty>, |
104 | /// The return type of the function being inferred. | 128 | /// The return type of the function being inferred. |
@@ -110,6 +134,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
110 | InferenceContext { | 134 | InferenceContext { |
111 | method_resolutions: FxHashMap::default(), | 135 | method_resolutions: FxHashMap::default(), |
112 | field_resolutions: FxHashMap::default(), | 136 | field_resolutions: FxHashMap::default(), |
137 | assoc_fn_resolutions: FxHashMap::default(), | ||
113 | type_of_expr: ArenaMap::default(), | 138 | type_of_expr: ArenaMap::default(), |
114 | type_of_pat: ArenaMap::default(), | 139 | type_of_pat: ArenaMap::default(), |
115 | var_unification_table: InPlaceUnificationTable::new(), | 140 | var_unification_table: InPlaceUnificationTable::new(), |
@@ -135,6 +160,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
135 | InferenceResult { | 160 | InferenceResult { |
136 | method_resolutions: self.method_resolutions, | 161 | method_resolutions: self.method_resolutions, |
137 | field_resolutions: self.field_resolutions, | 162 | field_resolutions: self.field_resolutions, |
163 | assoc_fn_resolutions: self.assoc_fn_resolutions, | ||
138 | type_of_expr: expr_types, | 164 | type_of_expr: expr_types, |
139 | type_of_pat: pat_types, | 165 | type_of_pat: pat_types, |
140 | } | 166 | } |
@@ -152,6 +178,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
152 | self.field_resolutions.insert(expr, field); | 178 | self.field_resolutions.insert(expr, field); |
153 | } | 179 | } |
154 | 180 | ||
181 | fn write_assoc_fn_resolution(&mut self, expr: ExprId, func: Function) { | ||
182 | self.assoc_fn_resolutions.insert(expr, func); | ||
183 | } | ||
184 | |||
155 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { | 185 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { |
156 | self.type_of_pat.insert(pat, ty); | 186 | self.type_of_pat.insert(pat, ty); |
157 | } | 187 | } |
@@ -341,7 +371,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
341 | }) | 371 | }) |
342 | } | 372 | } |
343 | 373 | ||
344 | fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> { | 374 | fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId) -> Option<Ty> { |
345 | let resolved = resolver.resolve_path_segments(self.db, &path); | 375 | let resolved = resolver.resolve_path_segments(self.db, &path); |
346 | 376 | ||
347 | let (def, remaining_index) = resolved.into_inner(); | 377 | let (def, remaining_index) = resolved.into_inner(); |
@@ -421,6 +451,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
421 | let typable: Option<TypableDef> = def.into(); | 451 | let typable: Option<TypableDef> = def.into(); |
422 | let typable = typable?; | 452 | let typable = typable?; |
423 | 453 | ||
454 | if let ExprOrPatId::Expr(expr) = id { | ||
455 | match typable { | ||
456 | TypableDef::Function(func) => self.write_assoc_fn_resolution(expr, func), | ||
457 | _ => {} | ||
458 | }; | ||
459 | } | ||
460 | |||
424 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | 461 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); |
425 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); | 462 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); |
426 | let ty = self.insert_type_vars(ty); | 463 | let ty = self.insert_type_vars(ty); |
@@ -572,7 +609,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
572 | Pat::Path(path) => { | 609 | Pat::Path(path) => { |
573 | // TODO use correct resolver for the surrounding expression | 610 | // TODO use correct resolver for the surrounding expression |
574 | let resolver = self.resolver.clone(); | 611 | let resolver = self.resolver.clone(); |
575 | self.infer_path_expr(&resolver, &path).unwrap_or(Ty::Unknown) | 612 | self.infer_path_expr(&resolver, &path, pat.into()).unwrap_or(Ty::Unknown) |
576 | } | 613 | } |
577 | Pat::Bind { mode, name: _name, subpat } => { | 614 | Pat::Bind { mode, name: _name, subpat } => { |
578 | let inner_ty = if let Some(subpat) = subpat { | 615 | let inner_ty = if let Some(subpat) = subpat { |
@@ -782,7 +819,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
782 | Expr::Path(p) => { | 819 | Expr::Path(p) => { |
783 | // TODO this could be more efficient... | 820 | // TODO this could be more efficient... |
784 | let resolver = expr::resolver_for_expr(self.body.clone(), self.db, tgt_expr); | 821 | let resolver = expr::resolver_for_expr(self.body.clone(), self.db, tgt_expr); |
785 | self.infer_path_expr(&resolver, p).unwrap_or(Ty::Unknown) | 822 | self.infer_path_expr(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown) |
786 | } | 823 | } |
787 | Expr::Continue => Ty::Never, | 824 | Expr::Continue => Ty::Never, |
788 | Expr::Break { expr } => { | 825 | Expr::Break { expr } => { |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 9ec179593..e4febe8cc 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -47,9 +47,10 @@ pub(crate) fn reference_definition( | |||
47 | name_ref: &ast::NameRef, | 47 | name_ref: &ast::NameRef, |
48 | ) -> ReferenceResult { | 48 | ) -> ReferenceResult { |
49 | use self::ReferenceResult::*; | 49 | use self::ReferenceResult::*; |
50 | if let Some(function) = | 50 | |
51 | hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax()) | 51 | let function = hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax()); |
52 | { | 52 | |
53 | if let Some(function) = function { | ||
53 | // Check if it is a method | 54 | // Check if it is a method |
54 | if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { | 55 | if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { |
55 | tested_by!(goto_definition_works_for_methods); | 56 | tested_by!(goto_definition_works_for_methods); |
@@ -122,9 +123,29 @@ pub(crate) fn reference_definition( | |||
122 | Some(Resolution::SelfType(_impl_block)) => { | 123 | Some(Resolution::SelfType(_impl_block)) => { |
123 | // TODO: go to the implemented type | 124 | // TODO: go to the implemented type |
124 | } | 125 | } |
125 | None => {} | 126 | None => { |
127 | // If we failed to resolve then check associated items | ||
128 | if let Some(function) = function { | ||
129 | // Should we do this above and then grab path from the PathExpr? | ||
130 | if let Some(path_expr) = | ||
131 | name_ref.syntax().ancestors().find_map(ast::PathExpr::cast) | ||
132 | { | ||
133 | let infer_result = function.infer(db); | ||
134 | let syntax_mapping = function.body_syntax_mapping(db); | ||
135 | let expr = ast::Expr::cast(path_expr.syntax()).unwrap(); | ||
136 | |||
137 | if let Some(func) = syntax_mapping | ||
138 | .node_expr(expr) | ||
139 | .and_then(|it| infer_result.assoc_fn_resolutions(it)) | ||
140 | { | ||
141 | return Exact(NavigationTarget::from_function(db, func)); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | } | ||
126 | } | 146 | } |
127 | } | 147 | } |
148 | |||
128 | // If that fails try the index based approach. | 149 | // If that fails try the index based approach. |
129 | let navs = crate::symbol_index::index_resolve(db, name_ref) | 150 | let navs = crate::symbol_index::index_resolve(db, name_ref) |
130 | .into_iter() | 151 | .into_iter() |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 8ec60090d..0f13777d7 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -512,4 +512,26 @@ mod tests { | |||
512 | let hover = analysis.hover(position).unwrap().unwrap(); | 512 | let hover = analysis.hover(position).unwrap().unwrap(); |
513 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); | 513 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); |
514 | } | 514 | } |
515 | |||
516 | #[test] | ||
517 | fn test_hover_infer_associated_method_exact() { | ||
518 | let (analysis, position) = single_file_with_position( | ||
519 | " | ||
520 | struct Thing { x: u32 } | ||
521 | |||
522 | impl Thing { | ||
523 | fn new() -> Thing { | ||
524 | Thing { x: 0 } | ||
525 | } | ||
526 | } | ||
527 | |||
528 | fn main() { | ||
529 | let foo_test = Thing::new<|>(); | ||
530 | } | ||
531 | ", | ||
532 | ); | ||
533 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
534 | assert_eq!(hover.info.first(), Some("```rust\nfn new() -> Thing\n```")); | ||
535 | assert_eq!(hover.info.is_exact(), true); | ||
536 | } | ||
515 | } | 537 | } |