diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-12 21:18:14 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-12 21:18:14 +0000 |
commit | eb931c0d9e0877e573622253ae5b05563841037b (patch) | |
tree | 653ef81450a4d39c5b46f98c97c23fa8586dd7f8 /crates/ra_hir/src | |
parent | e56072bfa3e5af69a4c293a38de6e1350ada3573 (diff) | |
parent | 1ed7fbfc1badd2c2a42b4dc2feb1b4bf7835d3ef (diff) |
Merge #505
505: Inherent methods r=matklad a=flodiebold
This adds resolution, type checking and completion for inherent methods.
The main open question here is the caching, I think. I'm not sure whether we should be caching method resolutions in a more fine grained way (currently we just build a hash map of types -> impl blocks, and iterate through all potential impl blocks when looking for a method).
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 20 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_impl/function.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_impl/function/scope.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_impl/module.rs | 15 | ||||
-rw-r--r-- | crates/ra_hir/src/db.rs | 9 | ||||
-rw-r--r-- | crates/ra_hir/src/expr.rs | 26 | ||||
-rw-r--r-- | crates/ra_hir/src/impl_block.rs | 36 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 47 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 164 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 26 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/inherent_method.txt | 18 |
12 files changed, 333 insertions, 42 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index d4244f70c..91b235594 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -113,6 +113,11 @@ impl Module { | |||
113 | self.child_impl(db, name) | 113 | self.child_impl(db, name) |
114 | } | 114 | } |
115 | 115 | ||
116 | /// Iterates over all child modules. | ||
117 | pub fn children(&self, db: &impl HirDatabase) -> Cancelable<impl Iterator<Item = Module>> { | ||
118 | self.children_impl(db) | ||
119 | } | ||
120 | |||
116 | /// Finds a parent module. | 121 | /// Finds a parent module. |
117 | pub fn parent(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> { | 122 | pub fn parent(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> { |
118 | self.parent_impl(db) | 123 | self.parent_impl(db) |
@@ -268,8 +273,11 @@ pub use crate::code_model_impl::function::ScopeEntryWithSyntax; | |||
268 | #[derive(Debug, Clone, PartialEq, Eq)] | 273 | #[derive(Debug, Clone, PartialEq, Eq)] |
269 | pub struct FnSignature { | 274 | pub struct FnSignature { |
270 | pub(crate) name: Name, | 275 | pub(crate) name: Name, |
271 | pub(crate) args: Vec<TypeRef>, | 276 | pub(crate) params: Vec<TypeRef>, |
272 | pub(crate) ret_type: TypeRef, | 277 | pub(crate) ret_type: TypeRef, |
278 | /// True if the first param is `self`. This is relevant to decide whether this | ||
279 | /// can be called as a method. | ||
280 | pub(crate) has_self_param: bool, | ||
273 | } | 281 | } |
274 | 282 | ||
275 | impl FnSignature { | 283 | impl FnSignature { |
@@ -277,13 +285,19 @@ impl FnSignature { | |||
277 | &self.name | 285 | &self.name |
278 | } | 286 | } |
279 | 287 | ||
280 | pub fn args(&self) -> &[TypeRef] { | 288 | pub fn params(&self) -> &[TypeRef] { |
281 | &self.args | 289 | &self.params |
282 | } | 290 | } |
283 | 291 | ||
284 | pub fn ret_type(&self) -> &TypeRef { | 292 | pub fn ret_type(&self) -> &TypeRef { |
285 | &self.ret_type | 293 | &self.ret_type |
286 | } | 294 | } |
295 | |||
296 | /// True if the first arg is `self`. This is relevant to decide whether this | ||
297 | /// can be called as a method. | ||
298 | pub fn has_self_param(&self) -> bool { | ||
299 | self.has_self_param | ||
300 | } | ||
287 | } | 301 | } |
288 | 302 | ||
289 | impl Function { | 303 | impl Function { |
diff --git a/crates/ra_hir/src/code_model_impl/function.rs b/crates/ra_hir/src/code_model_impl/function.rs index 1ce939e05..8d6b7fc19 100644 --- a/crates/ra_hir/src/code_model_impl/function.rs +++ b/crates/ra_hir/src/code_model_impl/function.rs | |||
@@ -42,7 +42,8 @@ impl FnSignature { | |||
42 | .name() | 42 | .name() |
43 | .map(|n| n.as_name()) | 43 | .map(|n| n.as_name()) |
44 | .unwrap_or_else(Name::missing); | 44 | .unwrap_or_else(Name::missing); |
45 | let mut args = Vec::new(); | 45 | let mut params = Vec::new(); |
46 | let mut has_self_param = false; | ||
46 | if let Some(param_list) = node.param_list() { | 47 | if let Some(param_list) = node.param_list() { |
47 | if let Some(self_param) = param_list.self_param() { | 48 | if let Some(self_param) = param_list.self_param() { |
48 | let self_type = if let Some(type_ref) = self_param.type_ref() { | 49 | let self_type = if let Some(type_ref) = self_param.type_ref() { |
@@ -59,11 +60,12 @@ impl FnSignature { | |||
59 | } | 60 | } |
60 | } | 61 | } |
61 | }; | 62 | }; |
62 | args.push(self_type); | 63 | params.push(self_type); |
64 | has_self_param = true; | ||
63 | } | 65 | } |
64 | for param in param_list.params() { | 66 | for param in param_list.params() { |
65 | let type_ref = TypeRef::from_ast_opt(param.type_ref()); | 67 | let type_ref = TypeRef::from_ast_opt(param.type_ref()); |
66 | args.push(type_ref); | 68 | params.push(type_ref); |
67 | } | 69 | } |
68 | } | 70 | } |
69 | let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { | 71 | let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { |
@@ -73,8 +75,9 @@ impl FnSignature { | |||
73 | }; | 75 | }; |
74 | let sig = FnSignature { | 76 | let sig = FnSignature { |
75 | name, | 77 | name, |
76 | args, | 78 | params, |
77 | ret_type, | 79 | ret_type, |
80 | has_self_param, | ||
78 | }; | 81 | }; |
79 | Arc::new(sig) | 82 | Arc::new(sig) |
80 | } | 83 | } |
diff --git a/crates/ra_hir/src/code_model_impl/function/scope.rs b/crates/ra_hir/src/code_model_impl/function/scope.rs index ebf6edc1b..7d938c0dd 100644 --- a/crates/ra_hir/src/code_model_impl/function/scope.rs +++ b/crates/ra_hir/src/code_model_impl/function/scope.rs | |||
@@ -43,7 +43,7 @@ impl FnScopes { | |||
43 | scope_for: FxHashMap::default(), | 43 | scope_for: FxHashMap::default(), |
44 | }; | 44 | }; |
45 | let root = scopes.root_scope(); | 45 | let root = scopes.root_scope(); |
46 | scopes.add_params_bindings(root, body.args()); | 46 | scopes.add_params_bindings(root, body.params()); |
47 | compute_expr_scopes(body.body_expr(), &body, &mut scopes, root); | 47 | compute_expr_scopes(body.body_expr(), &body, &mut scopes, root); |
48 | scopes | 48 | scopes |
49 | } | 49 | } |
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index e9ff06dc8..775dd6709 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs | |||
@@ -95,6 +95,21 @@ impl Module { | |||
95 | Module::from_module_id(db, loc.source_root_id, child_id).map(Some) | 95 | Module::from_module_id(db, loc.source_root_id, child_id).map(Some) |
96 | } | 96 | } |
97 | 97 | ||
98 | /// Iterates over all child modules. | ||
99 | pub fn children_impl(&self, db: &impl HirDatabase) -> Cancelable<impl Iterator<Item = Module>> { | ||
100 | // FIXME this should be implementable without collecting into a vec, but | ||
101 | // it's kind of hard since the iterator needs to keep a reference to the | ||
102 | // module tree. | ||
103 | let loc = self.def_id.loc(db); | ||
104 | let module_tree = db.module_tree(loc.source_root_id)?; | ||
105 | let children = loc | ||
106 | .module_id | ||
107 | .children(&module_tree) | ||
108 | .map(|(_, module_id)| Module::from_module_id(db, loc.source_root_id, module_id)) | ||
109 | .collect::<Cancelable<Vec<_>>>()?; | ||
110 | Ok(children.into_iter()) | ||
111 | } | ||
112 | |||
98 | pub fn parent_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> { | 113 | pub fn parent_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> { |
99 | let loc = self.def_id.loc(db); | 114 | let loc = self.def_id.loc(db); |
100 | let module_tree = db.module_tree(loc.source_root_id)?; | 115 | let module_tree = db.module_tree(loc.source_root_id)?; |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index a11c73db0..fd6336dd8 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -5,13 +5,13 @@ use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; | |||
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, | 7 | DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, |
8 | SourceFileItems, SourceItemId, | 8 | SourceFileItems, SourceItemId, Crate, |
9 | query_definitions, | 9 | query_definitions, |
10 | FnSignature, FnScopes, | 10 | FnSignature, FnScopes, |
11 | macros::MacroExpansion, | 11 | macros::MacroExpansion, |
12 | module_tree::{ModuleId, ModuleTree}, | 12 | module_tree::{ModuleId, ModuleTree}, |
13 | nameres::{ItemMap, InputModuleItems}, | 13 | nameres::{ItemMap, InputModuleItems}, |
14 | ty::{InferenceResult, Ty}, | 14 | ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks}, |
15 | adt::{StructData, EnumData, EnumVariantData}, | 15 | adt::{StructData, EnumData, EnumVariantData}, |
16 | impl_block::ModuleImplBlocks, | 16 | impl_block::ModuleImplBlocks, |
17 | }; | 17 | }; |
@@ -102,6 +102,11 @@ pub trait HirDatabase: SyntaxDatabase | |||
102 | use fn crate::impl_block::impls_in_module; | 102 | use fn crate::impl_block::impls_in_module; |
103 | } | 103 | } |
104 | 104 | ||
105 | fn impls_in_crate(krate: Crate) -> Cancelable<Arc<CrateImplBlocks>> { | ||
106 | type ImplsInCrateQuery; | ||
107 | use fn crate::ty::method_resolution::CrateImplBlocks::impls_in_crate_query; | ||
108 | } | ||
109 | |||
105 | fn body_hir(def_id: DefId) -> Cancelable<Arc<crate::expr::Body>> { | 110 | fn body_hir(def_id: DefId) -> Cancelable<Arc<crate::expr::Body>> { |
106 | type BodyHirQuery; | 111 | type BodyHirQuery; |
107 | use fn crate::expr::body_hir; | 112 | use fn crate::expr::body_hir; |
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index e5596cbaa..67e123e4d 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -18,13 +18,13 @@ impl_arena_id!(ExprId); | |||
18 | pub struct Body { | 18 | pub struct Body { |
19 | exprs: Arena<ExprId, Expr>, | 19 | exprs: Arena<ExprId, Expr>, |
20 | pats: Arena<PatId, Pat>, | 20 | pats: Arena<PatId, Pat>, |
21 | /// The patterns for the function's arguments. While the argument types are | 21 | /// The patterns for the function's parameters. While the parameter types are |
22 | /// part of the function signature, the patterns are not (they don't change | 22 | /// part of the function signature, the patterns are not (they don't change |
23 | /// the external type of the function). | 23 | /// the external type of the function). |
24 | /// | 24 | /// |
25 | /// If this `Body` is for the body of a constant, this will just be | 25 | /// If this `Body` is for the body of a constant, this will just be |
26 | /// empty. | 26 | /// empty. |
27 | args: Vec<PatId>, | 27 | params: Vec<PatId>, |
28 | /// The `ExprId` of the actual body expression. | 28 | /// The `ExprId` of the actual body expression. |
29 | body_expr: ExprId, | 29 | body_expr: ExprId, |
30 | } | 30 | } |
@@ -44,8 +44,8 @@ pub struct BodySyntaxMapping { | |||
44 | } | 44 | } |
45 | 45 | ||
46 | impl Body { | 46 | impl Body { |
47 | pub fn args(&self) -> &[PatId] { | 47 | pub fn params(&self) -> &[PatId] { |
48 | &self.args | 48 | &self.params |
49 | } | 49 | } |
50 | 50 | ||
51 | pub fn body_expr(&self) -> ExprId { | 51 | pub fn body_expr(&self) -> ExprId { |
@@ -699,11 +699,11 @@ impl ExprCollector { | |||
699 | } | 699 | } |
700 | } | 700 | } |
701 | 701 | ||
702 | fn into_body_syntax_mapping(self, args: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping { | 702 | fn into_body_syntax_mapping(self, params: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping { |
703 | let body = Body { | 703 | let body = Body { |
704 | exprs: self.exprs, | 704 | exprs: self.exprs, |
705 | pats: self.pats, | 705 | pats: self.pats, |
706 | args, | 706 | params, |
707 | body_expr, | 707 | body_expr, |
708 | }; | 708 | }; |
709 | BodySyntaxMapping { | 709 | BodySyntaxMapping { |
@@ -719,8 +719,8 @@ impl ExprCollector { | |||
719 | pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping { | 719 | pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping { |
720 | let mut collector = ExprCollector::new(); | 720 | let mut collector = ExprCollector::new(); |
721 | 721 | ||
722 | let args = if let Some(param_list) = node.param_list() { | 722 | let params = if let Some(param_list) = node.param_list() { |
723 | let mut args = Vec::new(); | 723 | let mut params = Vec::new(); |
724 | 724 | ||
725 | if let Some(self_param) = param_list.self_param() { | 725 | if let Some(self_param) = param_list.self_param() { |
726 | let self_param = LocalSyntaxPtr::new( | 726 | let self_param = LocalSyntaxPtr::new( |
@@ -729,13 +729,13 @@ pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping { | |||
729 | .expect("self param without self keyword") | 729 | .expect("self param without self keyword") |
730 | .syntax(), | 730 | .syntax(), |
731 | ); | 731 | ); |
732 | let arg = collector.alloc_pat( | 732 | let param = collector.alloc_pat( |
733 | Pat::Bind { | 733 | Pat::Bind { |
734 | name: Name::self_param(), | 734 | name: Name::self_param(), |
735 | }, | 735 | }, |
736 | self_param, | 736 | self_param, |
737 | ); | 737 | ); |
738 | args.push(arg); | 738 | params.push(param); |
739 | } | 739 | } |
740 | 740 | ||
741 | for param in param_list.params() { | 741 | for param in param_list.params() { |
@@ -744,15 +744,15 @@ pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping { | |||
744 | } else { | 744 | } else { |
745 | continue; | 745 | continue; |
746 | }; | 746 | }; |
747 | args.push(collector.collect_pat(pat)); | 747 | params.push(collector.collect_pat(pat)); |
748 | } | 748 | } |
749 | args | 749 | params |
750 | } else { | 750 | } else { |
751 | Vec::new() | 751 | Vec::new() |
752 | }; | 752 | }; |
753 | 753 | ||
754 | let body = collector.collect_block_opt(node.body()); | 754 | let body = collector.collect_block_opt(node.body()); |
755 | collector.into_body_syntax_mapping(args, body) | 755 | collector.into_body_syntax_mapping(params, body) |
756 | } | 756 | } |
757 | 757 | ||
758 | pub(crate) fn body_syntax_mapping( | 758 | pub(crate) fn body_syntax_mapping( |
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 4acda9af3..d0b086308 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs | |||
@@ -33,20 +33,27 @@ impl ImplBlock { | |||
33 | }) | 33 | }) |
34 | } | 34 | } |
35 | 35 | ||
36 | pub(crate) fn from_id(module_impl_blocks: Arc<ModuleImplBlocks>, impl_id: ImplId) -> ImplBlock { | ||
37 | ImplBlock { | ||
38 | module_impl_blocks, | ||
39 | impl_id, | ||
40 | } | ||
41 | } | ||
42 | |||
36 | fn impl_data(&self) -> &ImplData { | 43 | fn impl_data(&self) -> &ImplData { |
37 | &self.module_impl_blocks.impls[self.impl_id] | 44 | &self.module_impl_blocks.impls[self.impl_id] |
38 | } | 45 | } |
39 | 46 | ||
40 | pub fn target_trait(&self) -> Option<&TypeRef> { | 47 | pub fn target_trait(&self) -> Option<&TypeRef> { |
41 | self.impl_data().target_trait.as_ref() | 48 | self.impl_data().target_trait() |
42 | } | 49 | } |
43 | 50 | ||
44 | pub fn target_type(&self) -> &TypeRef { | 51 | pub fn target_type(&self) -> &TypeRef { |
45 | &self.impl_data().target_type | 52 | self.impl_data().target_type() |
46 | } | 53 | } |
47 | 54 | ||
48 | pub fn items(&self) -> &[ImplItem] { | 55 | pub fn items(&self) -> &[ImplItem] { |
49 | &self.impl_data().items | 56 | self.impl_data().items() |
50 | } | 57 | } |
51 | } | 58 | } |
52 | 59 | ||
@@ -64,7 +71,7 @@ impl ImplData { | |||
64 | module: &Module, | 71 | module: &Module, |
65 | node: &ast::ImplBlock, | 72 | node: &ast::ImplBlock, |
66 | ) -> Self { | 73 | ) -> Self { |
67 | let target_trait = node.target_type().map(TypeRef::from_ast); | 74 | let target_trait = node.target_trait().map(TypeRef::from_ast); |
68 | let target_type = TypeRef::from_ast_opt(node.target_type()); | 75 | let target_type = TypeRef::from_ast_opt(node.target_type()); |
69 | let module_loc = module.def_id.loc(db); | 76 | let module_loc = module.def_id.loc(db); |
70 | let items = if let Some(item_list) = node.item_list() { | 77 | let items = if let Some(item_list) = node.item_list() { |
@@ -103,6 +110,18 @@ impl ImplData { | |||
103 | items, | 110 | items, |
104 | } | 111 | } |
105 | } | 112 | } |
113 | |||
114 | pub fn target_trait(&self) -> Option<&TypeRef> { | ||
115 | self.target_trait.as_ref() | ||
116 | } | ||
117 | |||
118 | pub fn target_type(&self) -> &TypeRef { | ||
119 | &self.target_type | ||
120 | } | ||
121 | |||
122 | pub fn items(&self) -> &[ImplItem] { | ||
123 | &self.items | ||
124 | } | ||
106 | } | 125 | } |
107 | 126 | ||
108 | #[derive(Debug, Clone, PartialEq, Eq)] | 127 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -133,11 +152,9 @@ impl_arena_id!(ImplId); | |||
133 | /// This way, we avoid having to do this process for the whole crate whenever | 152 | /// This way, we avoid having to do this process for the whole crate whenever |
134 | /// a file is changed; as long as the impl blocks in the file don't change, | 153 | /// a file is changed; as long as the impl blocks in the file don't change, |
135 | /// we don't need to do the second step again. | 154 | /// we don't need to do the second step again. |
136 | /// | ||
137 | /// (The second step does not yet exist.) | ||
138 | #[derive(Debug, PartialEq, Eq)] | 155 | #[derive(Debug, PartialEq, Eq)] |
139 | pub struct ModuleImplBlocks { | 156 | pub struct ModuleImplBlocks { |
140 | impls: Arena<ImplId, ImplData>, | 157 | pub(crate) impls: Arena<ImplId, ImplData>, |
141 | impls_by_def: FxHashMap<DefId, ImplId>, | 158 | impls_by_def: FxHashMap<DefId, ImplId>, |
142 | } | 159 | } |
143 | 160 | ||
@@ -153,7 +170,10 @@ impl ModuleImplBlocks { | |||
153 | let (file_id, module_source) = module.definition_source(db)?; | 170 | let (file_id, module_source) = module.definition_source(db)?; |
154 | let node = match &module_source { | 171 | let node = match &module_source { |
155 | ModuleSource::SourceFile(node) => node.syntax(), | 172 | ModuleSource::SourceFile(node) => node.syntax(), |
156 | ModuleSource::Module(node) => node.syntax(), | 173 | ModuleSource::Module(node) => node |
174 | .item_list() | ||
175 | .expect("inline module should have item list") | ||
176 | .syntax(), | ||
157 | }; | 177 | }; |
158 | 178 | ||
159 | let source_file_items = db.file_items(file_id.into()); | 179 | let source_file_items = db.file_items(file_id.into()); |
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 6f93bb59d..9371c5a0d 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -235,6 +235,7 @@ salsa::database_storage! { | |||
235 | fn enum_data() for db::EnumDataQuery; | 235 | fn enum_data() for db::EnumDataQuery; |
236 | fn enum_variant_data() for db::EnumVariantDataQuery; | 236 | fn enum_variant_data() for db::EnumVariantDataQuery; |
237 | fn impls_in_module() for db::ImplsInModuleQuery; | 237 | fn impls_in_module() for db::ImplsInModuleQuery; |
238 | fn impls_in_crate() for db::ImplsInCrateQuery; | ||
238 | fn body_hir() for db::BodyHirQuery; | 239 | fn body_hir() for db::BodyHirQuery; |
239 | fn body_syntax_mapping() for db::BodySyntaxMappingQuery; | 240 | fn body_syntax_mapping() for db::BodySyntaxMappingQuery; |
240 | fn fn_signature() for db::FnSignatureQuery; | 241 | fn fn_signature() for db::FnSignatureQuery; |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 2d533eb6a..5d5568d69 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -17,6 +17,7 @@ mod autoderef; | |||
17 | mod primitive; | 17 | mod primitive; |
18 | #[cfg(test)] | 18 | #[cfg(test)] |
19 | mod tests; | 19 | mod tests; |
20 | pub(crate) mod method_resolution; | ||
20 | 21 | ||
21 | use std::borrow::Cow; | 22 | use std::borrow::Cow; |
22 | use std::ops::Index; | 23 | use std::ops::Index; |
@@ -431,7 +432,7 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { | |||
431 | let impl_block = f.impl_block(db)?; | 432 | let impl_block = f.impl_block(db)?; |
432 | // TODO we ignore type parameters for now | 433 | // TODO we ignore type parameters for now |
433 | let input = signature | 434 | let input = signature |
434 | .args() | 435 | .params() |
435 | .iter() | 436 | .iter() |
436 | .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), tr)) | 437 | .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), tr)) |
437 | .collect::<Cancelable<Vec<_>>>()?; | 438 | .collect::<Cancelable<Vec<_>>>()?; |
@@ -875,7 +876,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
875 | } | 876 | } |
876 | Expr::Call { callee, args } => { | 877 | Expr::Call { callee, args } => { |
877 | let callee_ty = self.infer_expr(*callee, &Expectation::none())?; | 878 | let callee_ty = self.infer_expr(*callee, &Expectation::none())?; |
878 | let (arg_tys, ret_ty) = match &callee_ty { | 879 | let (param_tys, ret_ty) = match &callee_ty { |
879 | Ty::FnPtr(sig) => (&sig.input[..], sig.output.clone()), | 880 | Ty::FnPtr(sig) => (&sig.input[..], sig.output.clone()), |
880 | _ => { | 881 | _ => { |
881 | // not callable | 882 | // not callable |
@@ -886,19 +887,43 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
886 | for (i, arg) in args.iter().enumerate() { | 887 | for (i, arg) in args.iter().enumerate() { |
887 | self.infer_expr( | 888 | self.infer_expr( |
888 | *arg, | 889 | *arg, |
889 | &Expectation::has_type(arg_tys.get(i).cloned().unwrap_or(Ty::Unknown)), | 890 | &Expectation::has_type(param_tys.get(i).cloned().unwrap_or(Ty::Unknown)), |
890 | )?; | 891 | )?; |
891 | } | 892 | } |
892 | ret_ty | 893 | ret_ty |
893 | } | 894 | } |
894 | Expr::MethodCall { receiver, args, .. } => { | 895 | Expr::MethodCall { |
895 | let _receiver_ty = self.infer_expr(*receiver, &Expectation::none())?; | 896 | receiver, |
896 | // TODO resolve method... | 897 | args, |
897 | for (_i, arg) in args.iter().enumerate() { | 898 | method_name, |
898 | // TODO unify / expect argument type | 899 | } => { |
899 | self.infer_expr(*arg, &Expectation::none())?; | 900 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none())?; |
901 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name)?; | ||
902 | let method_ty = match resolved { | ||
903 | Some(def_id) => self.db.type_for_def(def_id)?, | ||
904 | None => Ty::Unknown, | ||
905 | }; | ||
906 | let method_ty = self.insert_type_vars(method_ty); | ||
907 | let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { | ||
908 | Ty::FnPtr(sig) => { | ||
909 | if sig.input.len() > 0 { | ||
910 | (&sig.input[0], &sig.input[1..], sig.output.clone()) | ||
911 | } else { | ||
912 | (&Ty::Unknown, &[][..], sig.output.clone()) | ||
913 | } | ||
914 | } | ||
915 | _ => (&Ty::Unknown, &[][..], Ty::Unknown), | ||
916 | }; | ||
917 | // TODO we would have to apply the autoderef/autoref steps here | ||
918 | // to get the correct receiver type to unify... | ||
919 | self.unify(expected_receiver_ty, &receiver_ty); | ||
920 | for (i, arg) in args.iter().enumerate() { | ||
921 | self.infer_expr( | ||
922 | *arg, | ||
923 | &Expectation::has_type(param_tys.get(i).cloned().unwrap_or(Ty::Unknown)), | ||
924 | )?; | ||
900 | } | 925 | } |
901 | Ty::Unknown | 926 | ret_ty |
902 | } | 927 | } |
903 | Expr::Match { expr, arms } => { | 928 | Expr::Match { expr, arms } => { |
904 | let _ty = self.infer_expr(*expr, &Expectation::none())?; | 929 | let _ty = self.infer_expr(*expr, &Expectation::none())?; |
@@ -1068,7 +1093,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1068 | 1093 | ||
1069 | fn collect_fn_signature(&mut self, signature: &FnSignature) -> Cancelable<()> { | 1094 | fn collect_fn_signature(&mut self, signature: &FnSignature) -> Cancelable<()> { |
1070 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 1095 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
1071 | for (type_ref, pat) in signature.args().iter().zip(body.args()) { | 1096 | for (type_ref, pat) in signature.params().iter().zip(body.params()) { |
1072 | let ty = self.make_ty(type_ref)?; | 1097 | let ty = self.make_ty(type_ref)?; |
1073 | let ty = self.insert_type_vars(ty); | 1098 | let ty = self.insert_type_vars(ty); |
1074 | self.write_pat_ty(*pat, ty); | 1099 | self.write_pat_ty(*pat, ty); |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs new file mode 100644 index 000000000..7c3839388 --- /dev/null +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -0,0 +1,164 @@ | |||
1 | //! This module is concerned with finding methods that a given type provides. | ||
2 | //! For details about how this works in rustc, see the method lookup page in the | ||
3 | //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) | ||
4 | //! and the corresponding code mostly in librustc_typeck/check/method/probe.rs. | ||
5 | use std::sync::Arc; | ||
6 | |||
7 | use rustc_hash::FxHashMap; | ||
8 | |||
9 | use ra_db::{Cancelable, SourceRootId}; | ||
10 | |||
11 | use crate::{HirDatabase, DefId, module_tree::ModuleId, Module, Crate, Name, Function, impl_block::{ImplId, ImplBlock, ImplItem}}; | ||
12 | use super::Ty; | ||
13 | |||
14 | /// This is used as a key for indexing impls. | ||
15 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
16 | pub enum TyFingerprint { | ||
17 | Adt(DefId), | ||
18 | // we'll also want to index impls for primitive types etc. | ||
19 | } | ||
20 | |||
21 | impl TyFingerprint { | ||
22 | /// Creates a TyFingerprint for looking up an impl. Only certain types can | ||
23 | /// have impls: if we have some `struct S`, we can have an `impl S`, but not | ||
24 | /// `impl &S`. Hence, this will return `None` for reference types and such. | ||
25 | fn for_impl(ty: &Ty) -> Option<TyFingerprint> { | ||
26 | match ty { | ||
27 | Ty::Adt { def_id, .. } => Some(TyFingerprint::Adt(*def_id)), | ||
28 | _ => None, | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | |||
33 | #[derive(Debug, PartialEq, Eq)] | ||
34 | pub struct CrateImplBlocks { | ||
35 | /// To make sense of the ModuleIds, we need the source root. | ||
36 | source_root_id: SourceRootId, | ||
37 | impls: FxHashMap<TyFingerprint, Vec<(ModuleId, ImplId)>>, | ||
38 | } | ||
39 | |||
40 | impl CrateImplBlocks { | ||
41 | pub fn lookup_impl_blocks<'a>( | ||
42 | &'a self, | ||
43 | db: &'a impl HirDatabase, | ||
44 | ty: &Ty, | ||
45 | ) -> impl Iterator<Item = Cancelable<ImplBlock>> + 'a { | ||
46 | let fingerprint = TyFingerprint::for_impl(ty); | ||
47 | fingerprint | ||
48 | .and_then(|f| self.impls.get(&f)) | ||
49 | .into_iter() | ||
50 | .flat_map(|i| i.iter()) | ||
51 | .map(move |(module_id, impl_id)| { | ||
52 | let module_impl_blocks = db.impls_in_module(self.source_root_id, *module_id)?; | ||
53 | Ok(ImplBlock::from_id(module_impl_blocks, *impl_id)) | ||
54 | }) | ||
55 | } | ||
56 | |||
57 | fn collect_recursive(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> { | ||
58 | let module_id = module.def_id.loc(db).module_id; | ||
59 | let module_impl_blocks = db.impls_in_module(self.source_root_id, module_id)?; | ||
60 | |||
61 | for (impl_id, impl_data) in module_impl_blocks.impls.iter() { | ||
62 | let impl_block = ImplBlock::from_id(Arc::clone(&module_impl_blocks), impl_id); | ||
63 | |||
64 | if let Some(_target_trait) = impl_data.target_trait() { | ||
65 | // ignore for now | ||
66 | } else { | ||
67 | let target_ty = | ||
68 | Ty::from_hir(db, &module, Some(&impl_block), impl_data.target_type())?; | ||
69 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { | ||
70 | self.impls | ||
71 | .entry(target_ty_fp) | ||
72 | .or_insert_with(Vec::new) | ||
73 | .push((module_id, impl_id)); | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | for child in module.children(db)? { | ||
79 | self.collect_recursive(db, child)?; | ||
80 | } | ||
81 | |||
82 | Ok(()) | ||
83 | } | ||
84 | |||
85 | pub(crate) fn impls_in_crate_query( | ||
86 | db: &impl HirDatabase, | ||
87 | krate: Crate, | ||
88 | ) -> Cancelable<Arc<CrateImplBlocks>> { | ||
89 | let crate_graph = db.crate_graph(); | ||
90 | let file_id = crate_graph.crate_root(krate.crate_id); | ||
91 | let source_root_id = db.file_source_root(file_id); | ||
92 | let mut crate_impl_blocks = CrateImplBlocks { | ||
93 | source_root_id, | ||
94 | impls: FxHashMap::default(), | ||
95 | }; | ||
96 | if let Some(module) = krate.root_module(db)? { | ||
97 | crate_impl_blocks.collect_recursive(db, module)?; | ||
98 | } | ||
99 | Ok(Arc::new(crate_impl_blocks)) | ||
100 | } | ||
101 | } | ||
102 | |||
103 | fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Cancelable<Option<Crate>> { | ||
104 | match ty { | ||
105 | Ty::Adt { def_id, .. } => def_id.krate(db), | ||
106 | _ => Ok(None), | ||
107 | } | ||
108 | } | ||
109 | |||
110 | impl Ty { | ||
111 | // TODO: cache this as a query? | ||
112 | // - if so, what signature? (TyFingerprint, Name)? | ||
113 | // - or maybe cache all names and def_ids of methods per fingerprint? | ||
114 | pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<DefId>> { | ||
115 | self.iterate_methods(db, |f| { | ||
116 | let sig = f.signature(db); | ||
117 | if sig.name() == name && sig.has_self_param() { | ||
118 | Ok(Some(f.def_id())) | ||
119 | } else { | ||
120 | Ok(None) | ||
121 | } | ||
122 | }) | ||
123 | } | ||
124 | |||
125 | // This would be nicer if it just returned an iterator, but that's really | ||
126 | // complicated with all the cancelable operations | ||
127 | pub fn iterate_methods<T>( | ||
128 | self, | ||
129 | db: &impl HirDatabase, | ||
130 | mut callback: impl FnMut(Function) -> Cancelable<Option<T>>, | ||
131 | ) -> Cancelable<Option<T>> { | ||
132 | // For method calls, rust first does any number of autoderef, and then one | ||
133 | // autoref (i.e. when the method takes &self or &mut self). We just ignore | ||
134 | // the autoref currently -- when we find a method matching the given name, | ||
135 | // we assume it fits. | ||
136 | |||
137 | // Also note that when we've got a receiver like &S, even if the method we | ||
138 | // find in the end takes &self, we still do the autoderef step (just as | ||
139 | // rustc does an autoderef and then autoref again). | ||
140 | |||
141 | for derefed_ty in self.autoderef(db) { | ||
142 | let krate = match def_crate(db, &derefed_ty)? { | ||
143 | Some(krate) => krate, | ||
144 | None => continue, | ||
145 | }; | ||
146 | let impls = db.impls_in_crate(krate)?; | ||
147 | |||
148 | for impl_block in impls.lookup_impl_blocks(db, &derefed_ty) { | ||
149 | let impl_block = impl_block?; | ||
150 | for item in impl_block.items() { | ||
151 | match item { | ||
152 | ImplItem::Method(f) => { | ||
153 | if let Some(result) = callback(f.clone())? { | ||
154 | return Ok(Some(result)); | ||
155 | } | ||
156 | } | ||
157 | _ => {} | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | Ok(None) | ||
163 | } | ||
164 | } | ||
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 815aecda7..1c3129441 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -242,6 +242,32 @@ fn test() { | |||
242 | ); | 242 | ); |
243 | } | 243 | } |
244 | 244 | ||
245 | #[test] | ||
246 | fn infer_inherent_method() { | ||
247 | check_inference( | ||
248 | r#" | ||
249 | struct A; | ||
250 | |||
251 | impl A { | ||
252 | fn foo(self, x: u32) -> i32 {} | ||
253 | } | ||
254 | |||
255 | mod b { | ||
256 | impl super::A { | ||
257 | fn bar(&self, x: u64) -> i64 {} | ||
258 | } | ||
259 | } | ||
260 | |||
261 | fn test(a: A) { | ||
262 | a.foo(1); | ||
263 | (&a).bar(1); | ||
264 | a.bar(1); | ||
265 | } | ||
266 | "#, | ||
267 | "inherent_method.txt", | ||
268 | ); | ||
269 | } | ||
270 | |||
245 | fn infer(content: &str) -> String { | 271 | fn infer(content: &str) -> String { |
246 | let (db, _, file_id) = MockDatabase::with_single_file(content); | 272 | let (db, _, file_id) = MockDatabase::with_single_file(content); |
247 | let source_file = db.source_file(file_id); | 273 | let source_file = db.source_file(file_id); |
diff --git a/crates/ra_hir/src/ty/tests/data/inherent_method.txt b/crates/ra_hir/src/ty/tests/data/inherent_method.txt new file mode 100644 index 000000000..6e6f70357 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/inherent_method.txt | |||
@@ -0,0 +1,18 @@ | |||
1 | [32; 36) 'self': A | ||
2 | [38; 39) 'x': u32 | ||
3 | [53; 55) '{}': () | ||
4 | [103; 107) 'self': &A | ||
5 | [109; 110) 'x': u64 | ||
6 | [124; 126) '{}': () | ||
7 | [144; 145) 'a': A | ||
8 | [150; 198) '{ ...(1); }': () | ||
9 | [156; 157) 'a': A | ||
10 | [156; 164) 'a.foo(1)': i32 | ||
11 | [162; 163) '1': u32 | ||
12 | [170; 181) '(&a).bar(1)': i64 | ||
13 | [171; 173) '&a': &A | ||
14 | [172; 173) 'a': A | ||
15 | [179; 180) '1': u64 | ||
16 | [187; 188) 'a': A | ||
17 | [187; 195) 'a.bar(1)': i64 | ||
18 | [193; 194) '1': u64 | ||