diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-13 16:59:35 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-13 16:59:35 +0000 |
commit | 4209022e5b8dc185b098d76fd6701cd046b7c37d (patch) | |
tree | 98a579f8c0fd7a0738862937ec32a7dc62172e1a /crates | |
parent | 8b985b427c4e67d6833745258bfdf1c4e9eb62ca (diff) | |
parent | eedc08300c427b854db56f8fe1f1866ed398d5ee (diff) |
Merge #527
527: goto defenition works for type-inferred methods r=flodiebold a=matklad
This uses type inference results for `goto method` functionality.
This is achieved by adding another map to `InferenceResult`. I wonder how we should handle this long-term... The pattern seems to be "we are doing some analysis, and we produce some stuff as a by-product, and IDE would like to use the stuff". Ideally, adding an additional bit of info shouldn't require threading it through all data structures.
I kinda like how Kotlin deals with this problem. They have this [`BindingContext`](https://github.com/JetBrains/kotlin/blob/72e351a0e3610051fe4222dca4e1eeedf7ae45da/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java#L122) thing, which is basically an [`AnyMap`](https://github.com/JetBrains/kotlin/blob/72e351a0e3610051fe4222dca4e1eeedf7ae45da/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java#L122) of HashMaps.
Deep in the compiler guts, they [record the info](https://github.com/JetBrains/kotlin/blob/ba6da7c40a6cc502508faf6e04fa105b96bc7777/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForInvoke.java#L70-L75) into the map, using a type key, a value key and a value.
Then the IDE [reads this map](https://github.com/JetBrains/kotlin/blob/ba6da7c40a6cc502508faf6e04fa105b96bc7777/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantNotNullExtensionReceiverOfInlineInspection.kt#L64) (via a [helper](https://github.com/JetBrains/kotlin/blob/ba6da7c40a6cc502508faf6e04fa105b96bc7777/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/callUtil.kt#L178-L180)). The stuff in between does not know that this type-key exists, unless it inspects it.
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/ty.rs | 25 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 51 |
2 files changed, 71 insertions, 5 deletions
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 0692d3b2a..fa46ddfe9 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -28,6 +28,7 @@ use log; | |||
28 | use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError}; | 28 | use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError}; |
29 | use ra_arena::map::ArenaMap; | 29 | use ra_arena::map::ArenaMap; |
30 | use join_to_string::join; | 30 | use join_to_string::join; |
31 | use rustc_hash::FxHashMap; | ||
31 | 32 | ||
32 | use ra_db::Cancelable; | 33 | use ra_db::Cancelable; |
33 | 34 | ||
@@ -448,14 +449,14 @@ fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> { | |||
448 | }) | 449 | }) |
449 | } | 450 | } |
450 | 451 | ||
451 | pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> { | 452 | pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> { |
452 | Ok(Ty::Adt { | 453 | Ok(Ty::Adt { |
453 | def_id: s.def_id(), | 454 | def_id: s.def_id(), |
454 | name: s.name(db)?.unwrap_or_else(Name::missing), | 455 | name: s.name(db)?.unwrap_or_else(Name::missing), |
455 | }) | 456 | }) |
456 | } | 457 | } |
457 | 458 | ||
458 | pub fn type_for_enum_variant(db: &impl HirDatabase, ev: EnumVariant) -> Cancelable<Ty> { | 459 | pub(crate) fn type_for_enum_variant(db: &impl HirDatabase, ev: EnumVariant) -> Cancelable<Ty> { |
459 | let enum_parent = ev.parent_enum(db)?; | 460 | let enum_parent = ev.parent_enum(db)?; |
460 | 461 | ||
461 | type_for_enum(db, enum_parent) | 462 | type_for_enum(db, enum_parent) |
@@ -512,10 +513,18 @@ pub(super) fn type_for_field( | |||
512 | /// The result of type inference: A mapping from expressions and patterns to types. | 513 | /// The result of type inference: A mapping from expressions and patterns to types. |
513 | #[derive(Clone, PartialEq, Eq, Debug)] | 514 | #[derive(Clone, PartialEq, Eq, Debug)] |
514 | pub struct InferenceResult { | 515 | pub struct InferenceResult { |
516 | /// For each method call expr, record the function it resolved to. | ||
517 | method_resolutions: FxHashMap<ExprId, DefId>, | ||
515 | type_of_expr: ArenaMap<ExprId, Ty>, | 518 | type_of_expr: ArenaMap<ExprId, Ty>, |
516 | type_of_pat: ArenaMap<PatId, Ty>, | 519 | type_of_pat: ArenaMap<PatId, Ty>, |
517 | } | 520 | } |
518 | 521 | ||
522 | impl InferenceResult { | ||
523 | pub fn method_resolution(&self, expr: ExprId) -> Option<DefId> { | ||
524 | self.method_resolutions.get(&expr).map(|it| *it) | ||
525 | } | ||
526 | } | ||
527 | |||
519 | impl Index<ExprId> for InferenceResult { | 528 | impl Index<ExprId> for InferenceResult { |
520 | type Output = Ty; | 529 | type Output = Ty; |
521 | 530 | ||
@@ -541,6 +550,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
541 | module: Module, | 550 | module: Module, |
542 | impl_block: Option<ImplBlock>, | 551 | impl_block: Option<ImplBlock>, |
543 | var_unification_table: InPlaceUnificationTable<TypeVarId>, | 552 | var_unification_table: InPlaceUnificationTable<TypeVarId>, |
553 | method_resolutions: FxHashMap<ExprId, DefId>, | ||
544 | type_of_expr: ArenaMap<ExprId, Ty>, | 554 | type_of_expr: ArenaMap<ExprId, Ty>, |
545 | type_of_pat: ArenaMap<PatId, Ty>, | 555 | type_of_pat: ArenaMap<PatId, Ty>, |
546 | /// The return type of the function being inferred. | 556 | /// The return type of the function being inferred. |
@@ -631,6 +641,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
631 | impl_block: Option<ImplBlock>, | 641 | impl_block: Option<ImplBlock>, |
632 | ) -> Self { | 642 | ) -> Self { |
633 | InferenceContext { | 643 | InferenceContext { |
644 | method_resolutions: FxHashMap::default(), | ||
634 | type_of_expr: ArenaMap::default(), | 645 | type_of_expr: ArenaMap::default(), |
635 | type_of_pat: ArenaMap::default(), | 646 | type_of_pat: ArenaMap::default(), |
636 | var_unification_table: InPlaceUnificationTable::new(), | 647 | var_unification_table: InPlaceUnificationTable::new(), |
@@ -655,6 +666,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
655 | *ty = resolved; | 666 | *ty = resolved; |
656 | } | 667 | } |
657 | InferenceResult { | 668 | InferenceResult { |
669 | method_resolutions: mem::replace(&mut self.method_resolutions, Default::default()), | ||
658 | type_of_expr: expr_types, | 670 | type_of_expr: expr_types, |
659 | type_of_pat: pat_types, | 671 | type_of_pat: pat_types, |
660 | } | 672 | } |
@@ -664,6 +676,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
664 | self.type_of_expr.insert(expr, ty); | 676 | self.type_of_expr.insert(expr, ty); |
665 | } | 677 | } |
666 | 678 | ||
679 | fn write_method_resolution(&mut self, expr: ExprId, def_id: DefId) { | ||
680 | self.method_resolutions.insert(expr, def_id); | ||
681 | } | ||
682 | |||
667 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { | 683 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { |
668 | self.type_of_pat.insert(pat, ty); | 684 | self.type_of_pat.insert(pat, ty); |
669 | } | 685 | } |
@@ -900,7 +916,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
900 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none())?; | 916 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none())?; |
901 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name)?; | 917 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name)?; |
902 | let method_ty = match resolved { | 918 | let method_ty = match resolved { |
903 | Some(def_id) => self.db.type_for_def(def_id)?, | 919 | Some(def_id) => { |
920 | self.write_method_resolution(expr, def_id); | ||
921 | self.db.type_for_def(def_id)? | ||
922 | } | ||
904 | None => Ty::Unknown, | 923 | None => Ty::Unknown, |
905 | }; | 924 | }; |
906 | let method_ty = self.insert_type_vars(method_ty); | 925 | let method_ty = self.insert_type_vars(method_ty); |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index e2537758d..332a2fb8d 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -47,15 +47,34 @@ pub(crate) fn reference_definition( | |||
47 | name_ref: &ast::NameRef, | 47 | name_ref: &ast::NameRef, |
48 | ) -> Cancelable<ReferenceResult> { | 48 | ) -> Cancelable<ReferenceResult> { |
49 | use self::ReferenceResult::*; | 49 | use self::ReferenceResult::*; |
50 | if let Some(fn_descr) = | 50 | if let Some(function) = |
51 | hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())? | 51 | hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())? |
52 | { | 52 | { |
53 | let scope = fn_descr.scopes(db)?; | 53 | let scope = function.scopes(db)?; |
54 | // First try to resolve the symbol locally | 54 | // First try to resolve the symbol locally |
55 | if let Some(entry) = scope.resolve_local_name(name_ref) { | 55 | if let Some(entry) = scope.resolve_local_name(name_ref) { |
56 | let nav = NavigationTarget::from_scope_entry(file_id, &entry); | 56 | let nav = NavigationTarget::from_scope_entry(file_id, &entry); |
57 | return Ok(Exact(nav)); | 57 | return Ok(Exact(nav)); |
58 | }; | 58 | }; |
59 | |||
60 | // Next check if it is a method | ||
61 | if let Some(method_call) = name_ref | ||
62 | .syntax() | ||
63 | .parent() | ||
64 | .and_then(ast::MethodCallExpr::cast) | ||
65 | { | ||
66 | let infer_result = function.infer(db)?; | ||
67 | let syntax_mapping = function.body_syntax_mapping(db)?; | ||
68 | let expr = ast::Expr::cast(method_call.syntax()).unwrap(); | ||
69 | if let Some(def_id) = syntax_mapping | ||
70 | .node_expr(expr) | ||
71 | .and_then(|it| infer_result.method_resolution(it)) | ||
72 | { | ||
73 | if let Some(target) = NavigationTarget::from_def(db, def_id.resolve(db)?)? { | ||
74 | return Ok(Exact(target)); | ||
75 | } | ||
76 | }; | ||
77 | } | ||
59 | } | 78 | } |
60 | // Then try module name resolution | 79 | // Then try module name resolution |
61 | if let Some(module) = | 80 | if let Some(module) = |
@@ -167,4 +186,32 @@ mod tests { | |||
167 | "foo SOURCE_FILE FileId(2) [0; 10)", | 186 | "foo SOURCE_FILE FileId(2) [0; 10)", |
168 | ); | 187 | ); |
169 | } | 188 | } |
189 | |||
190 | #[test] | ||
191 | fn goto_definition_works_for_methods() { | ||
192 | check_goto( | ||
193 | " | ||
194 | //- /lib.rs | ||
195 | struct Foo; | ||
196 | impl Foo { | ||
197 | fn frobnicate(&self) { } | ||
198 | } | ||
199 | |||
200 | fn bar(foo: &Foo) { | ||
201 | foo.frobnicate<|>(); | ||
202 | } | ||
203 | ", | ||
204 | "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", | ||
205 | ); | ||
206 | |||
207 | check_goto( | ||
208 | " | ||
209 | //- /lib.rs | ||
210 | mod <|>foo; | ||
211 | //- /foo/mod.rs | ||
212 | // empty | ||
213 | ", | ||
214 | "foo SOURCE_FILE FileId(2) [0; 10)", | ||
215 | ); | ||
216 | } | ||
170 | } | 217 | } |