diff options
author | Florian Diebold <[email protected]> | 2019-01-07 12:44:54 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-01-12 14:01:19 +0000 |
commit | 082ef52bcb15d779c6aff78d9860d328bf7df9b2 (patch) | |
tree | a4a123ad491e15c8d17bdc815675d43bb33811b5 /crates | |
parent | e9e397e705ad0bec9775067b10109e35ebefc493 (diff) |
Implement basic inherent method resolution
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 14 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_impl/function.rs | 3 | ||||
-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/impl_block.rs | 36 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 39 | ||||
-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 | ||||
-rw-r--r-- | crates/ra_ide_api/src/db.rs | 1 |
11 files changed, 309 insertions, 17 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index d4244f70c..9c28b62af 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) |
@@ -270,6 +275,9 @@ pub struct FnSignature { | |||
270 | pub(crate) name: Name, | 275 | pub(crate) name: Name, |
271 | pub(crate) args: Vec<TypeRef>, | 276 | pub(crate) args: Vec<TypeRef>, |
272 | pub(crate) ret_type: TypeRef, | 277 | pub(crate) ret_type: TypeRef, |
278 | /// True if the first arg is `self`. This is relevant to decide whether this | ||
279 | /// can be called as a method. | ||
280 | pub(crate) has_self_arg: bool, | ||
273 | } | 281 | } |
274 | 282 | ||
275 | impl FnSignature { | 283 | impl FnSignature { |
@@ -284,6 +292,12 @@ impl FnSignature { | |||
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_arg(&self) -> bool { | ||
299 | self.has_self_arg | ||
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..77dddff79 100644 --- a/crates/ra_hir/src/code_model_impl/function.rs +++ b/crates/ra_hir/src/code_model_impl/function.rs | |||
@@ -43,6 +43,7 @@ impl FnSignature { | |||
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 args = Vec::new(); |
46 | let mut has_self_arg = 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() { |
@@ -60,6 +61,7 @@ impl FnSignature { | |||
60 | } | 61 | } |
61 | }; | 62 | }; |
62 | args.push(self_type); | 63 | args.push(self_type); |
64 | has_self_arg = 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()); |
@@ -75,6 +77,7 @@ impl FnSignature { | |||
75 | name, | 77 | name, |
76 | args, | 78 | args, |
77 | ret_type, | 79 | ret_type, |
80 | has_self_arg, | ||
78 | }; | 81 | }; |
79 | Arc::new(sig) | 82 | Arc::new(sig) |
80 | } | 83 | } |
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..842f54f11 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::impls_in_crate; | ||
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/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 4acda9af3..23c1d98d5 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) => match node.item_list() { |
174 | Some(item_list) => item_list.syntax(), | ||
175 | None => return Ok(()), | ||
176 | }, | ||
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..2dcba5283 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; |
@@ -891,14 +892,38 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
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, arg_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(arg_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())?; |
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..ad80aa8b6 --- /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 | |||
86 | pub(crate) fn impls_in_crate( | ||
87 | db: &impl HirDatabase, | ||
88 | krate: Crate, | ||
89 | ) -> Cancelable<Arc<CrateImplBlocks>> { | ||
90 | let crate_graph = db.crate_graph(); | ||
91 | let file_id = crate_graph.crate_root(krate.crate_id); | ||
92 | let source_root_id = db.file_source_root(file_id); | ||
93 | let mut crate_impl_blocks = CrateImplBlocks { | ||
94 | source_root_id, | ||
95 | impls: FxHashMap::default(), | ||
96 | }; | ||
97 | if let Some(module) = krate.root_module(db)? { | ||
98 | crate_impl_blocks.collect_recursive(db, module)?; | ||
99 | } | ||
100 | Ok(Arc::new(crate_impl_blocks)) | ||
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_arg() { | ||
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 | ||
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; |