aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-01-13 16:59:35 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-01-13 16:59:35 +0000
commit4209022e5b8dc185b098d76fd6701cd046b7c37d (patch)
tree98a579f8c0fd7a0738862937ec32a7dc62172e1a /crates
parent8b985b427c4e67d6833745258bfdf1c4e9eb62ca (diff)
parenteedc08300c427b854db56f8fe1f1866ed398d5ee (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.rs25
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs51
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;
28use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError}; 28use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError};
29use ra_arena::map::ArenaMap; 29use ra_arena::map::ArenaMap;
30use join_to_string::join; 30use join_to_string::join;
31use rustc_hash::FxHashMap;
31 32
32use ra_db::Cancelable; 33use 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
451pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> { 452pub(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
458pub fn type_for_enum_variant(db: &impl HirDatabase, ev: EnumVariant) -> Cancelable<Ty> { 459pub(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)]
514pub struct InferenceResult { 515pub 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
522impl InferenceResult {
523 pub fn method_resolution(&self, expr: ExprId) -> Option<DefId> {
524 self.method_resolutions.get(&expr).map(|it| *it)
525 }
526}
527
519impl Index<ExprId> for InferenceResult { 528impl 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}