aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-01-12 21:18:14 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-01-12 21:18:14 +0000
commiteb931c0d9e0877e573622253ae5b05563841037b (patch)
tree653ef81450a4d39c5b46f98c97c23fa8586dd7f8
parente56072bfa3e5af69a4c293a38de6e1350ada3573 (diff)
parent1ed7fbfc1badd2c2a42b4dc2feb1b4bf7835d3ef (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]>
-rw-r--r--crates/ra_hir/src/code_model_api.rs20
-rw-r--r--crates/ra_hir/src/code_model_impl/function.rs11
-rw-r--r--crates/ra_hir/src/code_model_impl/function/scope.rs2
-rw-r--r--crates/ra_hir/src/code_model_impl/module.rs15
-rw-r--r--crates/ra_hir/src/db.rs9
-rw-r--r--crates/ra_hir/src/expr.rs26
-rw-r--r--crates/ra_hir/src/impl_block.rs36
-rw-r--r--crates/ra_hir/src/mock.rs1
-rw-r--r--crates/ra_hir/src/ty.rs47
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs164
-rw-r--r--crates/ra_hir/src/ty/tests.rs26
-rw-r--r--crates/ra_hir/src/ty/tests/data/inherent_method.txt18
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs59
-rw-r--r--crates/ra_ide_api/src/completion/completion_item.rs9
-rw-r--r--crates/ra_ide_api/src/db.rs1
-rw-r--r--crates/ra_lsp_server/src/conv.rs1
16 files changed, 398 insertions, 47 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)]
269pub struct FnSignature { 274pub 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
275impl FnSignature { 283impl 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
289impl Function { 303impl 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
6use crate::{ 6use 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);
18pub struct Body { 18pub 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
46impl Body { 46impl 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 {
719pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping { 719pub(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
758pub(crate) fn body_syntax_mapping( 758pub(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)]
139pub struct ModuleImplBlocks { 156pub 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;
17mod primitive; 17mod primitive;
18#[cfg(test)] 18#[cfg(test)]
19mod tests; 19mod tests;
20pub(crate) mod method_resolution;
20 21
21use std::borrow::Cow; 22use std::borrow::Cow;
22use std::ops::Index; 23use 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.
5use std::sync::Arc;
6
7use rustc_hash::FxHashMap;
8
9use ra_db::{Cancelable, SourceRootId};
10
11use crate::{HirDatabase, DefId, module_tree::ModuleId, Module, Crate, Name, Function, impl_block::{ImplId, ImplBlock, ImplItem}};
12use super::Ty;
13
14/// This is used as a key for indexing impls.
15#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
16pub enum TyFingerprint {
17 Adt(DefId),
18 // we'll also want to index impls for primitive types etc.
19}
20
21impl 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)]
34pub 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
40impl 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
103fn 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
110impl 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]
246fn infer_inherent_method() {
247 check_inference(
248 r#"
249struct A;
250
251impl A {
252 fn foo(self, x: u32) -> i32 {}
253}
254
255mod b {
256 impl super::A {
257 fn bar(&self, x: u64) -> i64 {}
258 }
259}
260
261fn test(a: A) {
262 a.foo(1);
263 (&a).bar(1);
264 a.bar(1);
265}
266"#,
267 "inherent_method.txt",
268 );
269}
270
245fn infer(content: &str) -> String { 271fn 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
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index 80d0b1663..37985b398 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -17,8 +17,9 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Ca
17 }; 17 };
18 let receiver_ty = infer_result[expr].clone(); 18 let receiver_ty = infer_result[expr].clone();
19 if !ctx.is_call { 19 if !ctx.is_call {
20 complete_fields(acc, ctx, receiver_ty)?; 20 complete_fields(acc, ctx, receiver_ty.clone())?;
21 } 21 }
22 complete_methods(acc, ctx, receiver_ty)?;
22 Ok(()) 23 Ok(())
23} 24}
24 25
@@ -55,6 +56,24 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
55 Ok(()) 56 Ok(())
56} 57}
57 58
59fn complete_methods(
60 acc: &mut Completions,
61 ctx: &CompletionContext,
62 receiver: Ty,
63) -> Cancelable<()> {
64 receiver.iterate_methods(ctx.db, |func| {
65 let sig = func.signature(ctx.db);
66 if sig.has_self_param() {
67 CompletionItem::new(CompletionKind::Reference, sig.name().to_string())
68 .from_function(ctx, func)
69 .kind(CompletionItemKind::Method)
70 .add_to(acc);
71 }
72 Ok(None::<()>)
73 })?;
74 Ok(())
75}
76
58#[cfg(test)] 77#[cfg(test)]
59mod tests { 78mod tests {
60 use crate::completion::*; 79 use crate::completion::*;
@@ -87,7 +106,8 @@ mod tests {
87 } 106 }
88 } 107 }
89 ", 108 ",
90 r#"the_field "(u32,)""#, 109 r#"the_field "(u32,)"
110 foo "foo($0)""#,
91 ); 111 );
92 } 112 }
93 113
@@ -102,7 +122,8 @@ mod tests {
102 } 122 }
103 } 123 }
104 ", 124 ",
105 r#"the_field "(u32, i32)""#, 125 r#"the_field "(u32, i32)"
126 foo "foo($0)""#,
106 ); 127 );
107 } 128 }
108 129
@@ -118,4 +139,36 @@ mod tests {
118 r#""#, 139 r#""#,
119 ); 140 );
120 } 141 }
142
143 #[test]
144 fn test_method_completion() {
145 check_ref_completion(
146 r"
147 struct A {}
148 impl A {
149 fn the_method(&self) {}
150 }
151 fn foo(a: A) {
152 a.<|>
153 }
154 ",
155 r#"the_method "the_method($0)""#,
156 );
157 }
158
159 #[test]
160 fn test_no_non_self_method() {
161 check_ref_completion(
162 r"
163 struct A {}
164 impl A {
165 fn the_method() {}
166 }
167 fn foo(a: A) {
168 a.<|>
169 }
170 ",
171 r#""#,
172 );
173 }
121} 174}
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs
index e7fa967a0..b75d65de3 100644
--- a/crates/ra_ide_api/src/completion/completion_item.rs
+++ b/crates/ra_ide_api/src/completion/completion_item.rs
@@ -37,6 +37,7 @@ pub enum CompletionItemKind {
37 Const, 37 Const,
38 Trait, 38 Trait,
39 TypeAlias, 39 TypeAlias,
40 Method,
40} 41}
41 42
42#[derive(Debug, PartialEq, Eq)] 43#[derive(Debug, PartialEq, Eq)]
@@ -183,10 +184,14 @@ impl Builder {
183 self 184 self
184 } 185 }
185 186
186 fn from_function(mut self, ctx: &CompletionContext, function: hir::Function) -> Builder { 187 pub(super) fn from_function(
188 mut self,
189 ctx: &CompletionContext,
190 function: hir::Function,
191 ) -> Builder {
187 // If not an import, add parenthesis automatically. 192 // If not an import, add parenthesis automatically.
188 if ctx.use_item_syntax.is_none() && !ctx.is_call { 193 if ctx.use_item_syntax.is_none() && !ctx.is_call {
189 if function.signature(ctx.db).args().is_empty() { 194 if function.signature(ctx.db).params().is_empty() {
190 self.snippet = Some(format!("{}()$0", self.label)); 195 self.snippet = Some(format!("{}()$0", self.label));
191 } else { 196 } else {
192 self.snippet = Some(format!("{}($0)", self.label)); 197 self.snippet = Some(format!("{}($0)", self.label));
diff --git a/crates/ra_ide_api/src/db.rs b/crates/ra_ide_api/src/db.rs
index efdf261be..60f84675d 100644
--- a/crates/ra_ide_api/src/db.rs
+++ b/crates/ra_ide_api/src/db.rs
@@ -124,6 +124,7 @@ salsa::database_storage! {
124 fn enum_data() for hir::db::EnumDataQuery; 124 fn enum_data() for hir::db::EnumDataQuery;
125 fn enum_variant_data() for hir::db::EnumVariantDataQuery; 125 fn enum_variant_data() for hir::db::EnumVariantDataQuery;
126 fn impls_in_module() for hir::db::ImplsInModuleQuery; 126 fn impls_in_module() for hir::db::ImplsInModuleQuery;
127 fn impls_in_crate() for hir::db::ImplsInCrateQuery;
127 fn body_hir() for hir::db::BodyHirQuery; 128 fn body_hir() for hir::db::BodyHirQuery;
128 fn body_syntax_mapping() for hir::db::BodySyntaxMappingQuery; 129 fn body_syntax_mapping() for hir::db::BodySyntaxMappingQuery;
129 fn fn_signature() for hir::db::FnSignatureQuery; 130 fn fn_signature() for hir::db::FnSignatureQuery;
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 7ca2f437d..22b8e9221 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -69,6 +69,7 @@ impl Conv for CompletionItemKind {
69 CompletionItemKind::TypeAlias => Struct, 69 CompletionItemKind::TypeAlias => Struct,
70 CompletionItemKind::Const => Constant, 70 CompletionItemKind::Const => Constant,
71 CompletionItemKind::Static => Value, 71 CompletionItemKind::Static => Value,
72 CompletionItemKind::Method => Method,
72 } 73 }
73 } 74 }
74} 75}