aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-11-02 14:18:26 +0000
committerFlorian Diebold <[email protected]>2019-12-02 18:33:13 +0000
commitcbf262a1bc72f10dc93a4993da0012d3b0abb56f (patch)
tree5a45fdae6d7c2eba8488ef18c1ab2cc12ab8e270 /crates
parent3376c08052a563a5d2db487c458972378edebf44 (diff)
Change order of calls to get method candidate order correct
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs152
-rw-r--r--crates/ra_hir_ty/src/tests.rs2
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) = 227fn 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
254fn 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
274fn 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
227fn iterate_trait_method_candidates<T>( 294fn 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
273fn iterate_inherent_methods<T>( 340fn 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>(
298fn is_valid_candidate( 365fn 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]
3438fn method_resolution_trait_before_autoref() { 3437fn 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]
3454fn method_resolution_by_value_before_autoref() { 3452fn method_resolution_by_value_before_autoref() {
3455 let t = type_at( 3453 let t = type_at(