aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty/method_resolution.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty/method_resolution.rs')
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs126
1 files changed, 73 insertions, 53 deletions
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index 6b7918187..667b66095 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -129,85 +129,105 @@ impl Ty {
129 name: &Name, 129 name: &Name,
130 resolver: &Resolver, 130 resolver: &Resolver,
131 ) -> Option<(Ty, Function)> { 131 ) -> Option<(Ty, Function)> {
132 // FIXME: trait methods should be used before autoderefs 132 self.iterate_method_candidates(db, resolver, Some(name), |ty, f| Some((ty.clone(), f)))
133 // (and we need to do autoderefs for trait method calls as well)
134 let inherent_method = self.clone().iterate_methods(db, |ty, f| {
135 let sig = f.signature(db);
136 if sig.name() == name && sig.has_self_param() {
137 Some((ty.clone(), f))
138 } else {
139 None
140 }
141 });
142 inherent_method.or_else(|| self.lookup_trait_method(db, name, resolver))
143 } 133 }
144 134
145 fn lookup_trait_method( 135 // This would be nicer if it just returned an iterator, but that runs into
136 // lifetime problems, because we need to borrow temp `CrateImplBlocks`.
137 pub(crate) fn iterate_method_candidates<T>(
146 self, 138 self,
147 db: &impl HirDatabase, 139 db: &impl HirDatabase,
148 name: &Name,
149 resolver: &Resolver, 140 resolver: &Resolver,
150 ) -> Option<(Ty, Function)> { 141 name: Option<&Name>,
151 let mut candidates = Vec::new(); 142 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
152 for t in resolver.traits_in_scope() { 143 ) -> Option<T> {
144 // For method calls, rust first does any number of autoderef, and then one
145 // autoref (i.e. when the method takes &self or &mut self). We just ignore
146 // the autoref currently -- when we find a method matching the given name,
147 // we assume it fits.
148
149 // Also note that when we've got a receiver like &S, even if the method we
150 // find in the end takes &self, we still do the autoderef step (just as
151 // rustc does an autoderef and then autoref again).
152
153 for derefed_ty in self.autoderef(db) {
154 if let Some(result) = derefed_ty.iterate_inherent_methods(db, name, &mut callback) {
155 return Some(result);
156 }
157 if let Some(result) =
158 derefed_ty.iterate_trait_method_candidates(db, resolver, name, &mut callback)
159 {
160 return Some(result);
161 }
162 }
163 None
164 }
165
166 fn iterate_trait_method_candidates<T>(
167 &self,
168 db: &impl HirDatabase,
169 resolver: &Resolver,
170 name: Option<&Name>,
171 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
172 ) -> Option<T> {
173 'traits: for t in resolver.traits_in_scope() {
153 let data = t.trait_data(db); 174 let data = t.trait_data(db);
175 // we'll be lazy about checking whether the type implements the
176 // trait, but if we find out it doesn't, we'll skip the rest of the
177 // iteration
178 let mut known_implemented = false;
154 for item in data.items() { 179 for item in data.items() {
155 match item { 180 match item {
156 &TraitItem::Function(m) => { 181 &TraitItem::Function(m) => {
157 let sig = m.signature(db); 182 let sig = m.signature(db);
158 if sig.name() == name && sig.has_self_param() { 183 if name.map_or(true, |name| sig.name() == name) && sig.has_self_param() {
159 candidates.push((t, m)); 184 if !known_implemented {
185 let trait_ref = TraitRef {
186 trait_: t,
187 substs: fresh_substs_for_trait(db, t, self.clone()),
188 };
189 let (trait_ref, _) = super::traits::canonicalize(trait_ref);
190 if db.implements(trait_ref).is_none() {
191 continue 'traits;
192 }
193 }
194 known_implemented = true;
195 if let Some(result) = callback(self, m) {
196 return Some(result);
197 }
160 } 198 }
161 } 199 }
162 _ => {} 200 _ => {}
163 } 201 }
164 } 202 }
165 } 203 }
166 candidates.retain(|(t, _m)| { 204 None
167 let trait_ref =
168 TraitRef { trait_: *t, substs: fresh_substs_for_trait(db, *t, self.clone()) };
169 let (trait_ref, _) = super::traits::canonicalize(trait_ref);
170 db.implements(trait_ref).is_some()
171 });
172 // FIXME if there's multiple candidates here, that's an ambiguity error
173 let (_chosen_trait, chosen_method) = candidates.first()?;
174 // FIXME return correct receiver type
175 Some((self.clone(), *chosen_method))
176 } 205 }
177 206
178 // This would be nicer if it just returned an iterator, but that runs into 207 fn iterate_inherent_methods<T>(
179 // lifetime problems, because we need to borrow temp `CrateImplBlocks`. 208 &self,
180 pub fn iterate_methods<T>(
181 self,
182 db: &impl HirDatabase, 209 db: &impl HirDatabase,
210 name: Option<&Name>,
183 mut callback: impl FnMut(&Ty, Function) -> Option<T>, 211 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
184 ) -> Option<T> { 212 ) -> Option<T> {
185 // For method calls, rust first does any number of autoderef, and then one 213 let krate = match def_crate(db, self) {
186 // autoref (i.e. when the method takes &self or &mut self). We just ignore 214 Some(krate) => krate,
187 // the autoref currently -- when we find a method matching the given name, 215 None => return None,
188 // we assume it fits. 216 };
189 217 let impls = db.impls_in_crate(krate);
190 // Also note that when we've got a receiver like &S, even if the method we
191 // find in the end takes &self, we still do the autoderef step (just as
192 // rustc does an autoderef and then autoref again).
193 218
194 for derefed_ty in self.autoderef(db) { 219 for impl_block in impls.lookup_impl_blocks(self) {
195 let krate = match def_crate(db, &derefed_ty) { 220 for item in impl_block.items(db) {
196 Some(krate) => krate, 221 match item {
197 None => continue, 222 ImplItem::Method(f) => {
198 }; 223 let sig = f.signature(db);
199 let impls = db.impls_in_crate(krate); 224 if name.map_or(true, |name| sig.name() == name) && sig.has_self_param() {
200 225 if let Some(result) = callback(self, f) {
201 for impl_block in impls.lookup_impl_blocks(&derefed_ty) {
202 for item in impl_block.items(db) {
203 match item {
204 ImplItem::Method(f) => {
205 if let Some(result) = callback(&derefed_ty, f) {
206 return Some(result); 226 return Some(result);
207 } 227 }
208 } 228 }
209 _ => {}
210 } 229 }
230 _ => {}
211 } 231 }
212 } 232 }
213 } 233 }