diff options
Diffstat (limited to 'crates/ra_hir/src/ty/method_resolution.rs')
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 141 |
1 files changed, 100 insertions, 41 deletions
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index eb69344f6..8c3d32d09 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -166,37 +166,78 @@ pub(crate) fn lookup_method( | |||
166 | name: &Name, | 166 | name: &Name, |
167 | resolver: &Resolver, | 167 | resolver: &Resolver, |
168 | ) -> Option<(Ty, Function)> { | 168 | ) -> Option<(Ty, Function)> { |
169 | iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f))) | 169 | iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| match f |
170 | { | ||
171 | AssocItem::Function(f) => Some((ty.clone(), f)), | ||
172 | _ => None, | ||
173 | }) | ||
174 | } | ||
175 | |||
176 | /// Whether we're looking up a dotted method call (like `v.len()`) or a path | ||
177 | /// (like `Vec::new`). | ||
178 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
179 | pub enum LookupMode { | ||
180 | /// Looking up a method call like `v.len()`: We only consider candidates | ||
181 | /// that have a `self` parameter, and do autoderef. | ||
182 | MethodCall, | ||
183 | /// Looking up a path like `Vec::new` or `Vec::default`: We consider all | ||
184 | /// candidates including associated constants, but don't do autoderef. | ||
185 | Path, | ||
170 | } | 186 | } |
171 | 187 | ||
172 | // This would be nicer if it just returned an iterator, but that runs into | 188 | // This would be nicer if it just returned an iterator, but that runs into |
173 | // lifetime problems, because we need to borrow temp `CrateImplBlocks`. | 189 | // lifetime problems, because we need to borrow temp `CrateImplBlocks`. |
190 | // FIXME add a context type here? | ||
174 | pub(crate) fn iterate_method_candidates<T>( | 191 | pub(crate) fn iterate_method_candidates<T>( |
175 | ty: &Canonical<Ty>, | 192 | ty: &Canonical<Ty>, |
176 | db: &impl HirDatabase, | 193 | db: &impl HirDatabase, |
177 | resolver: &Resolver, | 194 | resolver: &Resolver, |
178 | name: Option<&Name>, | 195 | name: Option<&Name>, |
179 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 196 | mode: LookupMode, |
197 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
180 | ) -> Option<T> { | 198 | ) -> Option<T> { |
181 | // For method calls, rust first does any number of autoderef, and then one | 199 | let krate = resolver.krate()?; |
182 | // autoref (i.e. when the method takes &self or &mut self). We just ignore | 200 | match mode { |
183 | // the autoref currently -- when we find a method matching the given name, | 201 | LookupMode::MethodCall => { |
184 | // we assume it fits. | 202 | // For method calls, rust first does any number of autoderef, and then one |
203 | // autoref (i.e. when the method takes &self or &mut self). We just ignore | ||
204 | // the autoref currently -- when we find a method matching the given name, | ||
205 | // we assume it fits. | ||
185 | 206 | ||
186 | // Also note that when we've got a receiver like &S, even if the method we | 207 | // Also note that when we've got a receiver like &S, even if the method we |
187 | // find in the end takes &self, we still do the autoderef step (just as | 208 | // find in the end takes &self, we still do the autoderef step (just as |
188 | // rustc does an autoderef and then autoref again). | 209 | // rustc does an autoderef and then autoref again). |
189 | 210 | ||
190 | let krate = resolver.krate()?; | 211 | for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { |
191 | for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { | 212 | if let Some(result) = |
192 | if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) | 213 | iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) |
193 | { | 214 | { |
194 | return Some(result); | 215 | return Some(result); |
216 | } | ||
217 | if let Some(result) = iterate_trait_method_candidates( | ||
218 | &derefed_ty, | ||
219 | db, | ||
220 | resolver, | ||
221 | name, | ||
222 | mode, | ||
223 | &mut callback, | ||
224 | ) { | ||
225 | return Some(result); | ||
226 | } | ||
227 | } | ||
195 | } | 228 | } |
196 | if let Some(result) = | 229 | LookupMode::Path => { |
197 | iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback) | 230 | // No autoderef for path lookups |
198 | { | 231 | if let Some(result) = |
199 | return Some(result); | 232 | iterate_inherent_methods(&ty, db, name, mode, krate, &mut callback) |
233 | { | ||
234 | return Some(result); | ||
235 | } | ||
236 | if let Some(result) = | ||
237 | iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback) | ||
238 | { | ||
239 | return Some(result); | ||
240 | } | ||
200 | } | 241 | } |
201 | } | 242 | } |
202 | None | 243 | None |
@@ -207,7 +248,8 @@ fn iterate_trait_method_candidates<T>( | |||
207 | db: &impl HirDatabase, | 248 | db: &impl HirDatabase, |
208 | resolver: &Resolver, | 249 | resolver: &Resolver, |
209 | name: Option<&Name>, | 250 | name: Option<&Name>, |
210 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 251 | mode: LookupMode, |
252 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
211 | ) -> Option<T> { | 253 | ) -> Option<T> { |
212 | let krate = resolver.krate()?; | 254 | let krate = resolver.krate()?; |
213 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) | 255 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) |
@@ -231,22 +273,20 @@ fn iterate_trait_method_candidates<T>( | |||
231 | // trait, but if we find out it doesn't, we'll skip the rest of the | 273 | // trait, but if we find out it doesn't, we'll skip the rest of the |
232 | // iteration | 274 | // iteration |
233 | let mut known_implemented = inherently_implemented; | 275 | let mut known_implemented = inherently_implemented; |
234 | for item in data.items() { | 276 | for &item in data.items() { |
235 | if let AssocItem::Function(m) = *item { | 277 | if !is_valid_candidate(db, name, mode, item) { |
236 | let data = m.data(db); | 278 | continue; |
237 | if name.map_or(true, |name| data.name() == name) && data.has_self_param() { | 279 | } |
238 | if !known_implemented { | 280 | if !known_implemented { |
239 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); | 281 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); |
240 | if db.trait_solve(krate, goal).is_none() { | 282 | if db.trait_solve(krate, goal).is_none() { |
241 | continue 'traits; | 283 | continue 'traits; |
242 | } | ||
243 | } | ||
244 | known_implemented = true; | ||
245 | if let Some(result) = callback(&ty.value, m) { | ||
246 | return Some(result); | ||
247 | } | ||
248 | } | 284 | } |
249 | } | 285 | } |
286 | known_implemented = true; | ||
287 | if let Some(result) = callback(&ty.value, item) { | ||
288 | return Some(result); | ||
289 | } | ||
250 | } | 290 | } |
251 | } | 291 | } |
252 | None | 292 | None |
@@ -256,21 +296,20 @@ fn iterate_inherent_methods<T>( | |||
256 | ty: &Canonical<Ty>, | 296 | ty: &Canonical<Ty>, |
257 | db: &impl HirDatabase, | 297 | db: &impl HirDatabase, |
258 | name: Option<&Name>, | 298 | name: Option<&Name>, |
299 | mode: LookupMode, | ||
259 | krate: Crate, | 300 | krate: Crate, |
260 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 301 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, |
261 | ) -> Option<T> { | 302 | ) -> Option<T> { |
262 | for krate in def_crates(db, krate, &ty.value)? { | 303 | for krate in def_crates(db, krate, &ty.value)? { |
263 | let impls = db.impls_in_crate(krate); | 304 | let impls = db.impls_in_crate(krate); |
264 | 305 | ||
265 | for impl_block in impls.lookup_impl_blocks(&ty.value) { | 306 | for impl_block in impls.lookup_impl_blocks(&ty.value) { |
266 | for item in impl_block.items(db) { | 307 | for item in impl_block.items(db) { |
267 | if let AssocItem::Function(f) = item { | 308 | if !is_valid_candidate(db, name, mode, item) { |
268 | let data = f.data(db); | 309 | continue; |
269 | if name.map_or(true, |name| data.name() == name) && data.has_self_param() { | 310 | } |
270 | if let Some(result) = callback(&ty.value, f) { | 311 | if let Some(result) = callback(&ty.value, item) { |
271 | return Some(result); | 312 | return Some(result); |
272 | } | ||
273 | } | ||
274 | } | 313 | } |
275 | } | 314 | } |
276 | } | 315 | } |
@@ -278,6 +317,26 @@ fn iterate_inherent_methods<T>( | |||
278 | None | 317 | None |
279 | } | 318 | } |
280 | 319 | ||
320 | fn is_valid_candidate( | ||
321 | db: &impl HirDatabase, | ||
322 | name: Option<&Name>, | ||
323 | mode: LookupMode, | ||
324 | item: AssocItem, | ||
325 | ) -> bool { | ||
326 | match item { | ||
327 | AssocItem::Function(m) => { | ||
328 | let data = m.data(db); | ||
329 | name.map_or(true, |name| data.name() == name) | ||
330 | && (data.has_self_param() || mode == LookupMode::Path) | ||
331 | } | ||
332 | AssocItem::Const(c) => { | ||
333 | name.map_or(true, |name| Some(name) == c.name(db).as_ref()) | ||
334 | && (mode == LookupMode::Path) | ||
335 | } | ||
336 | _ => false, | ||
337 | } | ||
338 | } | ||
339 | |||
281 | pub(crate) fn implements_trait( | 340 | pub(crate) fn implements_trait( |
282 | ty: &Canonical<Ty>, | 341 | ty: &Canonical<Ty>, |
283 | db: &impl HirDatabase, | 342 | db: &impl HirDatabase, |