diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-03-05 07:30:19 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-03-05 07:30:19 +0000 |
commit | fe48f9f4d243aff59abc44e9ef9ccce9d43c01a8 (patch) | |
tree | 6f0c4e58df74819bf0793c93ca1ad7ae16d9f05c | |
parent | bbaf750b10810c21d42710a5d12181ca73099525 (diff) | |
parent | ac678473b85da24fc15315054e9737347fadb55c (diff) |
Merge #912
912: Make goto definition/hover work for associated items r=matklad a=kjeremy
Just functions so far. Looking for comments.
Fixes #911
Towards #832
Co-authored-by: kjeremy <[email protected]>
Co-authored-by: Jeremy Kolb <[email protected]>
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 73 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 29 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 22 | ||||
-rw-r--r-- | crates/ra_ide_api/src/navigation_target.rs | 16 |
4 files changed, 116 insertions, 24 deletions
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 5e4d49ffb..268d2c110 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -29,6 +29,7 @@ use crate::{ | |||
29 | Function, StructField, Path, Name, | 29 | Function, StructField, Path, Name, |
30 | FnSignature, AdtDef, | 30 | FnSignature, AdtDef, |
31 | HirDatabase, | 31 | HirDatabase, |
32 | ImplItem, | ||
32 | type_ref::{TypeRef, Mutability}, | 33 | type_ref::{TypeRef, Mutability}, |
33 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, | 34 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, |
34 | generics::GenericParams, | 35 | generics::GenericParams, |
@@ -54,6 +55,14 @@ pub fn infer(db: &impl HirDatabase, func: Function) -> Arc<InferenceResult> { | |||
54 | Arc::new(ctx.resolve_all()) | 55 | Arc::new(ctx.resolve_all()) |
55 | } | 56 | } |
56 | 57 | ||
58 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | ||
59 | enum ExprOrPatId { | ||
60 | ExprId(ExprId), | ||
61 | PatId(PatId), | ||
62 | } | ||
63 | |||
64 | impl_froms!(ExprOrPatId: ExprId, PatId); | ||
65 | |||
57 | /// The result of type inference: A mapping from expressions and patterns to types. | 66 | /// The result of type inference: A mapping from expressions and patterns to types. |
58 | #[derive(Clone, PartialEq, Eq, Debug)] | 67 | #[derive(Clone, PartialEq, Eq, Debug)] |
59 | pub struct InferenceResult { | 68 | pub struct InferenceResult { |
@@ -61,6 +70,8 @@ pub struct InferenceResult { | |||
61 | method_resolutions: FxHashMap<ExprId, Function>, | 70 | method_resolutions: FxHashMap<ExprId, Function>, |
62 | /// For each field access expr, records the field it resolves to. | 71 | /// For each field access expr, records the field it resolves to. |
63 | field_resolutions: FxHashMap<ExprId, StructField>, | 72 | field_resolutions: FxHashMap<ExprId, StructField>, |
73 | /// For each associated item record what it resolves to | ||
74 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | ||
64 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, | 75 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, |
65 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, | 76 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, |
66 | } | 77 | } |
@@ -72,6 +83,12 @@ impl InferenceResult { | |||
72 | pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> { | 83 | pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> { |
73 | self.field_resolutions.get(&expr).map(|it| *it) | 84 | self.field_resolutions.get(&expr).map(|it| *it) |
74 | } | 85 | } |
86 | pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<ImplItem> { | ||
87 | self.assoc_resolutions.get(&id.into()).map(|it| *it) | ||
88 | } | ||
89 | pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { | ||
90 | self.assoc_resolutions.get(&id.into()).map(|it| *it) | ||
91 | } | ||
75 | } | 92 | } |
76 | 93 | ||
77 | impl Index<ExprId> for InferenceResult { | 94 | impl Index<ExprId> for InferenceResult { |
@@ -99,6 +116,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
99 | var_unification_table: InPlaceUnificationTable<TypeVarId>, | 116 | var_unification_table: InPlaceUnificationTable<TypeVarId>, |
100 | method_resolutions: FxHashMap<ExprId, Function>, | 117 | method_resolutions: FxHashMap<ExprId, Function>, |
101 | field_resolutions: FxHashMap<ExprId, StructField>, | 118 | field_resolutions: FxHashMap<ExprId, StructField>, |
119 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | ||
102 | type_of_expr: ArenaMap<ExprId, Ty>, | 120 | type_of_expr: ArenaMap<ExprId, Ty>, |
103 | type_of_pat: ArenaMap<PatId, Ty>, | 121 | type_of_pat: ArenaMap<PatId, Ty>, |
104 | /// The return type of the function being inferred. | 122 | /// The return type of the function being inferred. |
@@ -110,6 +128,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
110 | InferenceContext { | 128 | InferenceContext { |
111 | method_resolutions: FxHashMap::default(), | 129 | method_resolutions: FxHashMap::default(), |
112 | field_resolutions: FxHashMap::default(), | 130 | field_resolutions: FxHashMap::default(), |
131 | assoc_resolutions: FxHashMap::default(), | ||
113 | type_of_expr: ArenaMap::default(), | 132 | type_of_expr: ArenaMap::default(), |
114 | type_of_pat: ArenaMap::default(), | 133 | type_of_pat: ArenaMap::default(), |
115 | var_unification_table: InPlaceUnificationTable::new(), | 134 | var_unification_table: InPlaceUnificationTable::new(), |
@@ -135,6 +154,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
135 | InferenceResult { | 154 | InferenceResult { |
136 | method_resolutions: self.method_resolutions, | 155 | method_resolutions: self.method_resolutions, |
137 | field_resolutions: self.field_resolutions, | 156 | field_resolutions: self.field_resolutions, |
157 | assoc_resolutions: self.assoc_resolutions, | ||
138 | type_of_expr: expr_types, | 158 | type_of_expr: expr_types, |
139 | type_of_pat: pat_types, | 159 | type_of_pat: pat_types, |
140 | } | 160 | } |
@@ -152,6 +172,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
152 | self.field_resolutions.insert(expr, field); | 172 | self.field_resolutions.insert(expr, field); |
153 | } | 173 | } |
154 | 174 | ||
175 | fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: ImplItem) { | ||
176 | self.assoc_resolutions.insert(id, item); | ||
177 | } | ||
178 | |||
155 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { | 179 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { |
156 | self.type_of_pat.insert(pat, ty); | 180 | self.type_of_pat.insert(pat, ty); |
157 | } | 181 | } |
@@ -341,7 +365,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
341 | }) | 365 | }) |
342 | } | 366 | } |
343 | 367 | ||
344 | fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> { | 368 | fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId) -> Option<Ty> { |
345 | let resolved = resolver.resolve_path_segments(self.db, &path); | 369 | let resolved = resolver.resolve_path_segments(self.db, &path); |
346 | 370 | ||
347 | let (def, remaining_index) = resolved.into_inner(); | 371 | let (def, remaining_index) = resolved.into_inner(); |
@@ -393,26 +417,38 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
393 | // Attempt to find an impl_item for the type which has a name matching | 417 | // Attempt to find an impl_item for the type which has a name matching |
394 | // the current segment | 418 | // the current segment |
395 | log::debug!("looking for path segment: {:?}", segment); | 419 | log::debug!("looking for path segment: {:?}", segment); |
396 | let item: crate::ModuleDef = ty.iterate_impl_items(self.db, |item| match item { | 420 | let item: crate::ModuleDef = ty.iterate_impl_items(self.db, |item| { |
397 | crate::ImplItem::Method(func) => { | 421 | let matching_def: Option<crate::ModuleDef> = match item { |
398 | let sig = func.signature(self.db); | 422 | crate::ImplItem::Method(func) => { |
399 | if segment.name == *sig.name() { | 423 | let sig = func.signature(self.db); |
400 | return Some(func.into()); | 424 | if segment.name == *sig.name() { |
425 | Some(func.into()) | ||
426 | } else { | ||
427 | None | ||
428 | } | ||
401 | } | 429 | } |
402 | None | ||
403 | } | ||
404 | 430 | ||
405 | crate::ImplItem::Const(konst) => { | 431 | crate::ImplItem::Const(konst) => { |
406 | let sig = konst.signature(self.db); | 432 | let sig = konst.signature(self.db); |
407 | if segment.name == *sig.name() { | 433 | if segment.name == *sig.name() { |
408 | return Some(konst.into()); | 434 | Some(konst.into()) |
435 | } else { | ||
436 | None | ||
437 | } | ||
409 | } | 438 | } |
410 | None | ||
411 | } | ||
412 | 439 | ||
413 | // TODO: Resolve associated types | 440 | // TODO: Resolve associated types |
414 | crate::ImplItem::TypeAlias(_) => None, | 441 | crate::ImplItem::TypeAlias(_) => None, |
442 | }; | ||
443 | match matching_def { | ||
444 | Some(_) => { | ||
445 | self.write_assoc_resolution(id, item); | ||
446 | return matching_def; | ||
447 | } | ||
448 | None => None, | ||
449 | } | ||
415 | })?; | 450 | })?; |
451 | |||
416 | resolved = Resolution::Def(item.into()); | 452 | resolved = Resolution::Def(item.into()); |
417 | } | 453 | } |
418 | 454 | ||
@@ -420,7 +456,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
420 | Resolution::Def(def) => { | 456 | Resolution::Def(def) => { |
421 | let typable: Option<TypableDef> = def.into(); | 457 | let typable: Option<TypableDef> = def.into(); |
422 | let typable = typable?; | 458 | let typable = typable?; |
423 | |||
424 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | 459 | 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); | 460 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); |
426 | let ty = self.insert_type_vars(ty); | 461 | let ty = self.insert_type_vars(ty); |
@@ -572,7 +607,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
572 | Pat::Path(path) => { | 607 | Pat::Path(path) => { |
573 | // TODO use correct resolver for the surrounding expression | 608 | // TODO use correct resolver for the surrounding expression |
574 | let resolver = self.resolver.clone(); | 609 | let resolver = self.resolver.clone(); |
575 | self.infer_path_expr(&resolver, &path).unwrap_or(Ty::Unknown) | 610 | self.infer_path_expr(&resolver, &path, pat.into()).unwrap_or(Ty::Unknown) |
576 | } | 611 | } |
577 | Pat::Bind { mode, name: _name, subpat } => { | 612 | Pat::Bind { mode, name: _name, subpat } => { |
578 | let inner_ty = if let Some(subpat) = subpat { | 613 | let inner_ty = if let Some(subpat) = subpat { |
@@ -782,7 +817,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
782 | Expr::Path(p) => { | 817 | Expr::Path(p) => { |
783 | // TODO this could be more efficient... | 818 | // TODO this could be more efficient... |
784 | let resolver = expr::resolver_for_expr(self.body.clone(), self.db, tgt_expr); | 819 | let resolver = expr::resolver_for_expr(self.body.clone(), self.db, tgt_expr); |
785 | self.infer_path_expr(&resolver, p).unwrap_or(Ty::Unknown) | 820 | self.infer_path_expr(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown) |
786 | } | 821 | } |
787 | Expr::Continue => Ty::Never, | 822 | Expr::Continue => Ty::Never, |
788 | Expr::Break { expr } => { | 823 | 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..364263d9b 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 source_map = function.body_source_map(db); | ||
135 | let expr = ast::Expr::cast(path_expr.syntax()).unwrap(); | ||
136 | |||
137 | if let Some(res) = source_map | ||
138 | .node_expr(expr) | ||
139 | .and_then(|it| infer_result.assoc_resolutions_for_expr(it.into())) | ||
140 | { | ||
141 | return Exact(NavigationTarget::from_impl_item(db, res)); | ||
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..bcd052c8b 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!(trim_markup_opt(hover.info.first()), Some("fn new() -> Thing")); | ||
535 | assert_eq!(hover.info.is_exact(), true); | ||
536 | } | ||
515 | } | 537 | } |
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index 6538081ac..d806cb368 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs | |||
@@ -3,7 +3,7 @@ use ra_syntax::{ | |||
3 | SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast, | 3 | SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast, |
4 | SyntaxKind::{self, NAME}, | 4 | SyntaxKind::{self, NAME}, |
5 | }; | 5 | }; |
6 | use hir::{ModuleSource, FieldSource, Name}; | 6 | use hir::{ModuleSource, FieldSource, Name, ImplItem}; |
7 | 7 | ||
8 | use crate::{FileSymbol, db::RootDatabase}; | 8 | use crate::{FileSymbol, db::RootDatabase}; |
9 | 9 | ||
@@ -174,6 +174,20 @@ impl NavigationTarget { | |||
174 | ) | 174 | ) |
175 | } | 175 | } |
176 | 176 | ||
177 | pub(crate) fn from_impl_item(db: &RootDatabase, impl_item: hir::ImplItem) -> NavigationTarget { | ||
178 | match impl_item { | ||
179 | ImplItem::Method(f) => NavigationTarget::from_function(db, f), | ||
180 | ImplItem::Const(c) => { | ||
181 | let (file_id, node) = c.source(db); | ||
182 | NavigationTarget::from_named(file_id.original_file(db), &*node) | ||
183 | } | ||
184 | ImplItem::TypeAlias(a) => { | ||
185 | let (file_id, node) = a.source(db); | ||
186 | NavigationTarget::from_named(file_id.original_file(db), &*node) | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
177 | #[cfg(test)] | 191 | #[cfg(test)] |
178 | pub(crate) fn assert_match(&self, expected: &str) { | 192 | pub(crate) fn assert_match(&self, expected: &str) { |
179 | let actual = self.debug_render(); | 193 | let actual = self.debug_render(); |