diff options
author | Florian Diebold <[email protected]> | 2019-11-02 14:18:26 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-12-02 18:33:13 +0000 |
commit | cbf262a1bc72f10dc93a4993da0012d3b0abb56f (patch) | |
tree | 5a45fdae6d7c2eba8488ef18c1ab2cc12ab8e270 | |
parent | 3376c08052a563a5d2db487c458972378edebf44 (diff) |
Change order of calls to get method candidate order correct
-rw-r--r-- | crates/ra_hir_ty/src/method_resolution.rs | 152 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests.rs | 2 |
2 files changed, 115 insertions, 39 deletions
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 2bded3dbd..fbb932a3e 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs | |||
@@ -176,7 +176,6 @@ pub fn iterate_method_candidates<T>( | |||
176 | mode: LookupMode, | 176 | mode: LookupMode, |
177 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 177 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
178 | ) -> Option<T> { | 178 | ) -> Option<T> { |
179 | let krate = resolver.krate()?; | ||
180 | match mode { | 179 | match mode { |
181 | LookupMode::MethodCall => { | 180 | LookupMode::MethodCall => { |
182 | // For method calls, rust first does any number of autoderef, and then one | 181 | // For method calls, rust first does any number of autoderef, and then one |
@@ -189,57 +188,125 @@ pub fn iterate_method_candidates<T>( | |||
189 | // rustc does an autoderef and then autoref again). | 188 | // rustc does an autoderef and then autoref again). |
190 | let environment = TraitEnvironment::lower(db, resolver); | 189 | let environment = TraitEnvironment::lower(db, resolver); |
191 | let ty = InEnvironment { value: ty.clone(), environment }; | 190 | let ty = InEnvironment { value: ty.clone(), environment }; |
192 | for derefed_ty in autoderef::autoderef(db, resolver.krate(), ty) { | 191 | let krate = resolver.krate()?; |
193 | if let Some(result) = | 192 | |
194 | iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) | 193 | // We have to be careful about the order of operations here. |
195 | { | 194 | // Consider the case where we're resolving `x.clone()` where `x: |
196 | return Some(result); | 195 | // &Vec<_>`. This resolves to the clone method with self type |
197 | } | 196 | // `Vec<_>`, *not* `&_`. I.e. we need to consider methods where the |
198 | if let Some(result) = iterate_trait_method_candidates( | 197 | // receiver type exactly matches before cases where we have to do |
199 | &derefed_ty, | 198 | // autoref. But in the autoderef steps, the `&_` self type comes up |
199 | // *before* the `Vec<_>` self type. | ||
200 | // | ||
201 | // On the other hand, we don't want to just pick any by-value method | ||
202 | // before any by-autoref method; it's just that we need to consider | ||
203 | // the methods by autoderef order of *receiver types*, not *self | ||
204 | // types*. | ||
205 | |||
206 | let deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty.clone()).collect(); | ||
207 | for i in 0..deref_chain.len() { | ||
208 | if let Some(result) = iterate_method_candidates_autoref( | ||
209 | &deref_chain[i..], | ||
200 | db, | 210 | db, |
201 | resolver, | 211 | resolver, |
202 | name, | 212 | name, |
203 | mode, | ||
204 | &mut callback, | 213 | &mut callback, |
205 | ) { | 214 | ) { |
206 | return Some(result); | 215 | return Some(result); |
207 | } | 216 | } |
208 | } | 217 | } |
218 | None | ||
209 | } | 219 | } |
210 | LookupMode::Path => { | 220 | LookupMode::Path => { |
211 | // No autoderef for path lookups | 221 | // No autoderef for path lookups |
212 | if let Some(result) = | 222 | iterate_method_candidates_inner(&ty, db, resolver, name, None, &mut callback) |
213 | iterate_inherent_methods(&ty, db, name, mode, krate.into(), &mut callback) | 223 | } |
214 | { | 224 | } |
215 | return Some(result); | 225 | } |
216 | } | 226 | |
217 | if let Some(result) = | 227 | fn iterate_method_candidates_autoref<T>( |
218 | iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback) | 228 | deref_chain: &[Canonical<Ty>], |
219 | { | 229 | db: &impl HirDatabase, |
220 | return Some(result); | 230 | resolver: &Resolver, |
221 | } | 231 | name: Option<&Name>, |
232 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | ||
233 | ) -> Option<T> { | ||
234 | if let Some(result) = iterate_method_candidates_by_receiver(&deref_chain[0], &deref_chain[1..], db, resolver, name, &mut callback) { | ||
235 | return Some(result); | ||
236 | } | ||
237 | let refed = Canonical { | ||
238 | num_vars: deref_chain[0].num_vars, | ||
239 | value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()), | ||
240 | }; | ||
241 | if let Some(result) = iterate_method_candidates_by_receiver(&refed, deref_chain, db, resolver, name, &mut callback) { | ||
242 | return Some(result); | ||
243 | } | ||
244 | let ref_muted = Canonical { | ||
245 | num_vars: deref_chain[0].num_vars, | ||
246 | value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()), | ||
247 | }; | ||
248 | if let Some(result) = iterate_method_candidates_by_receiver(&ref_muted, deref_chain, db, resolver, name, &mut callback) { | ||
249 | return Some(result); | ||
250 | } | ||
251 | None | ||
252 | } | ||
253 | |||
254 | fn iterate_method_candidates_by_receiver<T>( | ||
255 | receiver_ty: &Canonical<Ty>, | ||
256 | deref_chain: &[Canonical<Ty>], | ||
257 | db: &impl HirDatabase, | ||
258 | resolver: &Resolver, | ||
259 | name: Option<&Name>, | ||
260 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | ||
261 | ) -> Option<T> { | ||
262 | // TODO: do we need to do the whole loop for inherents before traits? | ||
263 | // We're looking for methods with *receiver* type receiver_ty. These could | ||
264 | // be found in any of the derefs of receiver_ty, so we have to go through | ||
265 | // that. | ||
266 | for self_ty in std::iter::once(receiver_ty).chain(deref_chain) { | ||
267 | if let Some(result) = iterate_method_candidates_inner(self_ty, db, resolver, name, Some(receiver_ty), &mut callback) { | ||
268 | return Some(result); | ||
222 | } | 269 | } |
223 | } | 270 | } |
224 | None | 271 | None |
225 | } | 272 | } |
226 | 273 | ||
274 | fn iterate_method_candidates_inner<T>( | ||
275 | self_ty: &Canonical<Ty>, | ||
276 | db: &impl HirDatabase, | ||
277 | resolver: &Resolver, | ||
278 | name: Option<&Name>, | ||
279 | receiver_ty: Option<&Canonical<Ty>>, | ||
280 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | ||
281 | ) -> Option<T> { | ||
282 | let krate = resolver.krate()?; | ||
283 | if let Some(result) = iterate_inherent_methods(self_ty, db, name, receiver_ty, krate, &mut callback) { | ||
284 | return Some(result); | ||
285 | } | ||
286 | if let Some(result) = | ||
287 | iterate_trait_method_candidates(self_ty, db, resolver, name, receiver_ty, &mut callback) | ||
288 | { | ||
289 | return Some(result); | ||
290 | } | ||
291 | None | ||
292 | } | ||
293 | |||
227 | fn iterate_trait_method_candidates<T>( | 294 | fn iterate_trait_method_candidates<T>( |
228 | ty: &Canonical<Ty>, | 295 | self_ty: &Canonical<Ty>, |
229 | db: &impl HirDatabase, | 296 | db: &impl HirDatabase, |
230 | resolver: &Resolver, | 297 | resolver: &Resolver, |
231 | name: Option<&Name>, | 298 | name: Option<&Name>, |
232 | mode: LookupMode, | 299 | receiver_ty: Option<&Canonical<Ty>>, |
233 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 300 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
234 | ) -> Option<T> { | 301 | ) -> Option<T> { |
235 | let krate = resolver.krate()?; | 302 | let krate = resolver.krate()?; |
236 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) | 303 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) |
237 | let env = TraitEnvironment::lower(db, resolver); | 304 | let env = TraitEnvironment::lower(db, resolver); |
238 | // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope | 305 | // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope |
239 | let inherent_trait = ty.value.inherent_trait().into_iter(); | 306 | let inherent_trait = self_ty.value.inherent_trait().into_iter(); |
240 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope | 307 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope |
241 | let traits_from_env = env | 308 | let traits_from_env = env |
242 | .trait_predicates_for_self_ty(&ty.value) | 309 | .trait_predicates_for_self_ty(&self_ty.value) |
243 | .map(|tr| tr.trait_) | 310 | .map(|tr| tr.trait_) |
244 | .flat_map(|t| all_super_traits(db, t)); | 311 | .flat_map(|t| all_super_traits(db, t)); |
245 | let traits = | 312 | let traits = |
@@ -252,17 +319,17 @@ fn iterate_trait_method_candidates<T>( | |||
252 | // iteration | 319 | // iteration |
253 | let mut known_implemented = false; | 320 | let mut known_implemented = false; |
254 | for (_name, item) in data.items.iter() { | 321 | for (_name, item) in data.items.iter() { |
255 | if !is_valid_candidate(db, name, mode, (*item).into()) { | 322 | if !is_valid_candidate(db, name, receiver_ty, (*item).into(), self_ty) { |
256 | continue; | 323 | continue; |
257 | } | 324 | } |
258 | if !known_implemented { | 325 | if !known_implemented { |
259 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); | 326 | let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); |
260 | if db.trait_solve(krate.into(), goal).is_none() { | 327 | if db.trait_solve(krate.into(), goal).is_none() { |
261 | continue 'traits; | 328 | continue 'traits; |
262 | } | 329 | } |
263 | } | 330 | } |
264 | known_implemented = true; | 331 | known_implemented = true; |
265 | if let Some(result) = callback(&ty.value, (*item).into()) { | 332 | if let Some(result) = callback(&self_ty.value, (*item).into()) { |
266 | return Some(result); | 333 | return Some(result); |
267 | } | 334 | } |
268 | } | 335 | } |
@@ -271,22 +338,22 @@ fn iterate_trait_method_candidates<T>( | |||
271 | } | 338 | } |
272 | 339 | ||
273 | fn iterate_inherent_methods<T>( | 340 | fn iterate_inherent_methods<T>( |
274 | ty: &Canonical<Ty>, | 341 | self_ty: &Canonical<Ty>, |
275 | db: &impl HirDatabase, | 342 | db: &impl HirDatabase, |
276 | name: Option<&Name>, | 343 | name: Option<&Name>, |
277 | mode: LookupMode, | 344 | receiver_ty: Option<&Canonical<Ty>>, |
278 | krate: CrateId, | 345 | krate: CrateId, |
279 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 346 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
280 | ) -> Option<T> { | 347 | ) -> Option<T> { |
281 | for krate in ty.value.def_crates(db, krate)? { | 348 | for krate in self_ty.value.def_crates(db, krate)? { |
282 | let impls = db.impls_in_crate(krate); | 349 | let impls = db.impls_in_crate(krate); |
283 | 350 | ||
284 | for impl_block in impls.lookup_impl_blocks(&ty.value) { | 351 | for impl_block in impls.lookup_impl_blocks(&self_ty.value) { |
285 | for &item in db.impl_data(impl_block).items.iter() { | 352 | for &item in db.impl_data(impl_block).items.iter() { |
286 | if !is_valid_candidate(db, name, mode, item) { | 353 | if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { |
287 | continue; | 354 | continue; |
288 | } | 355 | } |
289 | if let Some(result) = callback(&ty.value, item.into()) { | 356 | if let Some(result) = callback(&self_ty.value, item) { |
290 | return Some(result); | 357 | return Some(result); |
291 | } | 358 | } |
292 | } | 359 | } |
@@ -298,18 +365,29 @@ fn iterate_inherent_methods<T>( | |||
298 | fn is_valid_candidate( | 365 | fn is_valid_candidate( |
299 | db: &impl HirDatabase, | 366 | db: &impl HirDatabase, |
300 | name: Option<&Name>, | 367 | name: Option<&Name>, |
301 | mode: LookupMode, | 368 | receiver_ty: Option<&Canonical<Ty>>, |
302 | item: AssocItemId, | 369 | item: AssocItemId, |
370 | self_ty: &Canonical<Ty>, | ||
303 | ) -> bool { | 371 | ) -> bool { |
304 | match item { | 372 | match item { |
305 | AssocItemId::FunctionId(m) => { | 373 | AssocItemId::FunctionId(m) => { |
306 | let data = db.function_data(m); | 374 | let data = db.function_data(m); |
307 | name.map_or(true, |name| &data.name == name) | 375 | if let Some(name) = name { |
308 | && (data.has_self_param || mode == LookupMode::Path) | 376 | if &data.name != name { |
377 | return false; | ||
378 | } | ||
379 | } | ||
380 | if let Some(receiver_ty) = receiver_ty { | ||
381 | if !data.has_self_param { | ||
382 | return false; | ||
383 | } | ||
384 | // TODO compare receiver ty | ||
385 | } | ||
386 | true | ||
309 | } | 387 | } |
310 | AssocItemId::ConstId(c) => { | 388 | AssocItemId::ConstId(c) => { |
311 | let data = db.const_data(c); | 389 | let data = db.const_data(c); |
312 | name.map_or(true, |name| data.name.as_ref() == Some(name)) && (mode == LookupMode::Path) | 390 | name.map_or(true, |name| data.name.as_ref() == Some(name)) && receiver_ty.is_none() |
313 | } | 391 | } |
314 | _ => false, | 392 | _ => false, |
315 | } | 393 | } |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index a3cc5cf95..be8768c62 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -3433,7 +3433,6 @@ pub fn baz() -> usize { 31usize } | |||
3433 | assert_eq!("(i32, usize)", type_at_pos(&db, pos)); | 3433 | assert_eq!("(i32, usize)", type_at_pos(&db, pos)); |
3434 | } | 3434 | } |
3435 | 3435 | ||
3436 | #[ignore] | ||
3437 | #[test] | 3436 | #[test] |
3438 | fn method_resolution_trait_before_autoref() { | 3437 | fn method_resolution_trait_before_autoref() { |
3439 | let t = type_at( | 3438 | let t = type_at( |
@@ -3449,7 +3448,6 @@ fn test() { S.foo()<|>; } | |||
3449 | assert_eq!(t, "u128"); | 3448 | assert_eq!(t, "u128"); |
3450 | } | 3449 | } |
3451 | 3450 | ||
3452 | #[ignore] | ||
3453 | #[test] | 3451 | #[test] |
3454 | fn method_resolution_by_value_before_autoref() { | 3452 | fn method_resolution_by_value_before_autoref() { |
3455 | let t = type_at( | 3453 | let t = type_at( |