aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty/src')
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs42
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs130
-rw-r--r--crates/ra_hir_ty/src/tests.rs16
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs82
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs2
5 files changed, 196 insertions, 76 deletions
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 4006f595d..4dd4f9802 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -4,7 +4,7 @@ use std::iter::repeat;
4use std::sync::Arc; 4use std::sync::Arc;
5 5
6use hir_def::{ 6use hir_def::{
7 expr::{BindingAnnotation, Pat, PatId, RecordFieldPat}, 7 expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat},
8 path::Path, 8 path::Path,
9 type_ref::Mutability, 9 type_ref::Mutability,
10 FieldId, 10 FieldId,
@@ -90,18 +90,7 @@ impl<'a> InferenceContext<'a> {
90 ) -> Ty { 90 ) -> Ty {
91 let body = Arc::clone(&self.body); // avoid borrow checker problem 91 let body = Arc::clone(&self.body); // avoid borrow checker problem
92 92
93 let is_non_ref_pat = match &body[pat] { 93 if is_non_ref_pat(&body, pat) {
94 Pat::Tuple { .. }
95 | Pat::Or(..)
96 | Pat::TupleStruct { .. }
97 | Pat::Record { .. }
98 | Pat::Range { .. }
99 | Pat::Slice { .. } => true,
100 // FIXME: Path/Lit might actually evaluate to ref, but inference is unimplemented.
101 Pat::Path(..) | Pat::Lit(..) => true,
102 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Missing => false,
103 };
104 if is_non_ref_pat {
105 while let Some((inner, mutability)) = expected.as_reference() { 94 while let Some((inner, mutability)) = expected.as_reference() {
106 expected = inner; 95 expected = inner;
107 default_bm = match default_bm { 96 default_bm = match default_bm {
@@ -195,7 +184,7 @@ impl<'a> InferenceContext<'a> {
195 self.write_pat_ty(pat, bound_ty); 184 self.write_pat_ty(pat, bound_ty);
196 return inner_ty; 185 return inner_ty;
197 } 186 }
198 Pat::Slice { prefix, slice: _slice, suffix } => { 187 Pat::Slice { prefix, slice, suffix } => {
199 let (container_ty, elem_ty) = match &expected { 188 let (container_ty, elem_ty) = match &expected {
200 ty_app!(TypeCtor::Array, st) => (TypeCtor::Array, st.as_single().clone()), 189 ty_app!(TypeCtor::Array, st) => (TypeCtor::Array, st.as_single().clone()),
201 ty_app!(TypeCtor::Slice, st) => (TypeCtor::Slice, st.as_single().clone()), 190 ty_app!(TypeCtor::Slice, st) => (TypeCtor::Slice, st.as_single().clone()),
@@ -206,7 +195,12 @@ impl<'a> InferenceContext<'a> {
206 self.infer_pat(*pat_id, &elem_ty, default_bm); 195 self.infer_pat(*pat_id, &elem_ty, default_bm);
207 } 196 }
208 197
209 Ty::apply_one(container_ty, elem_ty) 198 let pat_ty = Ty::apply_one(container_ty, elem_ty);
199 if let Some(slice_pat_id) = slice {
200 self.infer_pat(*slice_pat_id, &pat_ty, default_bm);
201 }
202
203 pat_ty
210 } 204 }
211 Pat::Wild => expected.clone(), 205 Pat::Wild => expected.clone(),
212 Pat::Range { start, end } => { 206 Pat::Range { start, end } => {
@@ -227,3 +221,21 @@ impl<'a> InferenceContext<'a> {
227 ty 221 ty
228 } 222 }
229} 223}
224
225fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
226 match &body[pat] {
227 Pat::Tuple { .. }
228 | Pat::TupleStruct { .. }
229 | Pat::Record { .. }
230 | Pat::Range { .. }
231 | Pat::Slice { .. } => true,
232 Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)),
233 // FIXME: Path/Lit might actually evaluate to ref, but inference is unimplemented.
234 Pat::Path(..) => true,
235 Pat::Lit(expr) => match body[*expr] {
236 Expr::Literal(Literal::String(..)) => false,
237 _ => true,
238 },
239 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Missing => false,
240 }
241}
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index ed638c195..c19519cf1 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -271,6 +271,34 @@ pub fn iterate_method_candidates<T>(
271 mode: LookupMode, 271 mode: LookupMode,
272 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 272 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
273) -> Option<T> { 273) -> Option<T> {
274 let mut slot = None;
275 iterate_method_candidates_impl(
276 ty,
277 db,
278 env,
279 krate,
280 traits_in_scope,
281 name,
282 mode,
283 &mut |ty, item| {
284 assert!(slot.is_none());
285 slot = callback(ty, item);
286 slot.is_some()
287 },
288 );
289 slot
290}
291
292fn iterate_method_candidates_impl(
293 ty: &Canonical<Ty>,
294 db: &dyn HirDatabase,
295 env: Arc<TraitEnvironment>,
296 krate: CrateId,
297 traits_in_scope: &FxHashSet<TraitId>,
298 name: Option<&Name>,
299 mode: LookupMode,
300 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
301) -> bool {
274 match mode { 302 match mode {
275 LookupMode::MethodCall => { 303 LookupMode::MethodCall => {
276 // For method calls, rust first does any number of autoderef, and then one 304 // For method calls, rust first does any number of autoderef, and then one
@@ -298,19 +326,19 @@ pub fn iterate_method_candidates<T>(
298 326
299 let deref_chain = autoderef_method_receiver(db, krate, ty); 327 let deref_chain = autoderef_method_receiver(db, krate, ty);
300 for i in 0..deref_chain.len() { 328 for i in 0..deref_chain.len() {
301 if let Some(result) = iterate_method_candidates_with_autoref( 329 if iterate_method_candidates_with_autoref(
302 &deref_chain[i..], 330 &deref_chain[i..],
303 db, 331 db,
304 env.clone(), 332 env.clone(),
305 krate, 333 krate,
306 traits_in_scope, 334 traits_in_scope,
307 name, 335 name,
308 &mut callback, 336 callback,
309 ) { 337 ) {
310 return Some(result); 338 return true;
311 } 339 }
312 } 340 }
313 None 341 false
314 } 342 }
315 LookupMode::Path => { 343 LookupMode::Path => {
316 // No autoderef for path lookups 344 // No autoderef for path lookups
@@ -321,22 +349,22 @@ pub fn iterate_method_candidates<T>(
321 krate, 349 krate,
322 traits_in_scope, 350 traits_in_scope,
323 name, 351 name,
324 &mut callback, 352 callback,
325 ) 353 )
326 } 354 }
327 } 355 }
328} 356}
329 357
330fn iterate_method_candidates_with_autoref<T>( 358fn iterate_method_candidates_with_autoref(
331 deref_chain: &[Canonical<Ty>], 359 deref_chain: &[Canonical<Ty>],
332 db: &dyn HirDatabase, 360 db: &dyn HirDatabase,
333 env: Arc<TraitEnvironment>, 361 env: Arc<TraitEnvironment>,
334 krate: CrateId, 362 krate: CrateId,
335 traits_in_scope: &FxHashSet<TraitId>, 363 traits_in_scope: &FxHashSet<TraitId>,
336 name: Option<&Name>, 364 name: Option<&Name>,
337 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 365 mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
338) -> Option<T> { 366) -> bool {
339 if let Some(result) = iterate_method_candidates_by_receiver( 367 if iterate_method_candidates_by_receiver(
340 &deref_chain[0], 368 &deref_chain[0],
341 &deref_chain[1..], 369 &deref_chain[1..],
342 db, 370 db,
@@ -346,13 +374,13 @@ fn iterate_method_candidates_with_autoref<T>(
346 name, 374 name,
347 &mut callback, 375 &mut callback,
348 ) { 376 ) {
349 return Some(result); 377 return true;
350 } 378 }
351 let refed = Canonical { 379 let refed = Canonical {
352 num_vars: deref_chain[0].num_vars, 380 num_vars: deref_chain[0].num_vars,
353 value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()), 381 value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
354 }; 382 };
355 if let Some(result) = iterate_method_candidates_by_receiver( 383 if iterate_method_candidates_by_receiver(
356 &refed, 384 &refed,
357 deref_chain, 385 deref_chain,
358 db, 386 db,
@@ -362,13 +390,13 @@ fn iterate_method_candidates_with_autoref<T>(
362 name, 390 name,
363 &mut callback, 391 &mut callback,
364 ) { 392 ) {
365 return Some(result); 393 return true;
366 } 394 }
367 let ref_muted = Canonical { 395 let ref_muted = Canonical {
368 num_vars: deref_chain[0].num_vars, 396 num_vars: deref_chain[0].num_vars,
369 value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()), 397 value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
370 }; 398 };
371 if let Some(result) = iterate_method_candidates_by_receiver( 399 if iterate_method_candidates_by_receiver(
372 &ref_muted, 400 &ref_muted,
373 deref_chain, 401 deref_chain,
374 db, 402 db,
@@ -378,12 +406,12 @@ fn iterate_method_candidates_with_autoref<T>(
378 name, 406 name,
379 &mut callback, 407 &mut callback,
380 ) { 408 ) {
381 return Some(result); 409 return true;
382 } 410 }
383 None 411 false
384} 412}
385 413
386fn iterate_method_candidates_by_receiver<T>( 414fn iterate_method_candidates_by_receiver(
387 receiver_ty: &Canonical<Ty>, 415 receiver_ty: &Canonical<Ty>,
388 rest_of_deref_chain: &[Canonical<Ty>], 416 rest_of_deref_chain: &[Canonical<Ty>],
389 db: &dyn HirDatabase, 417 db: &dyn HirDatabase,
@@ -391,20 +419,18 @@ fn iterate_method_candidates_by_receiver<T>(
391 krate: CrateId, 419 krate: CrateId,
392 traits_in_scope: &FxHashSet<TraitId>, 420 traits_in_scope: &FxHashSet<TraitId>,
393 name: Option<&Name>, 421 name: Option<&Name>,
394 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 422 mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
395) -> Option<T> { 423) -> bool {
396 // We're looking for methods with *receiver* type receiver_ty. These could 424 // We're looking for methods with *receiver* type receiver_ty. These could
397 // be found in any of the derefs of receiver_ty, so we have to go through 425 // be found in any of the derefs of receiver_ty, so we have to go through
398 // that. 426 // that.
399 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) { 427 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
400 if let Some(result) = 428 if iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback) {
401 iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback) 429 return true;
402 {
403 return Some(result);
404 } 430 }
405 } 431 }
406 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) { 432 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
407 if let Some(result) = iterate_trait_method_candidates( 433 if iterate_trait_method_candidates(
408 self_ty, 434 self_ty,
409 db, 435 db,
410 env.clone(), 436 env.clone(),
@@ -414,40 +440,28 @@ fn iterate_method_candidates_by_receiver<T>(
414 Some(receiver_ty), 440 Some(receiver_ty),
415 &mut callback, 441 &mut callback,
416 ) { 442 ) {
417 return Some(result); 443 return true;
418 } 444 }
419 } 445 }
420 None 446 false
421} 447}
422 448
423fn iterate_method_candidates_for_self_ty<T>( 449fn iterate_method_candidates_for_self_ty(
424 self_ty: &Canonical<Ty>, 450 self_ty: &Canonical<Ty>,
425 db: &dyn HirDatabase, 451 db: &dyn HirDatabase,
426 env: Arc<TraitEnvironment>, 452 env: Arc<TraitEnvironment>,
427 krate: CrateId, 453 krate: CrateId,
428 traits_in_scope: &FxHashSet<TraitId>, 454 traits_in_scope: &FxHashSet<TraitId>,
429 name: Option<&Name>, 455 name: Option<&Name>,
430 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 456 mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
431) -> Option<T> { 457) -> bool {
432 if let Some(result) = iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) { 458 if iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) {
433 return Some(result); 459 return true;
434 }
435 if let Some(result) = iterate_trait_method_candidates(
436 self_ty,
437 db,
438 env,
439 krate,
440 traits_in_scope,
441 name,
442 None,
443 &mut callback,
444 ) {
445 return Some(result);
446 } 460 }
447 None 461 iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback)
448} 462}
449 463
450fn iterate_trait_method_candidates<T>( 464fn iterate_trait_method_candidates(
451 self_ty: &Canonical<Ty>, 465 self_ty: &Canonical<Ty>,
452 db: &dyn HirDatabase, 466 db: &dyn HirDatabase,
453 env: Arc<TraitEnvironment>, 467 env: Arc<TraitEnvironment>,
@@ -455,8 +469,8 @@ fn iterate_trait_method_candidates<T>(
455 traits_in_scope: &FxHashSet<TraitId>, 469 traits_in_scope: &FxHashSet<TraitId>,
456 name: Option<&Name>, 470 name: Option<&Name>,
457 receiver_ty: Option<&Canonical<Ty>>, 471 receiver_ty: Option<&Canonical<Ty>>,
458 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 472 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
459) -> Option<T> { 473) -> bool {
460 // if ty is `dyn Trait`, the trait doesn't need to be in scope 474 // if ty is `dyn Trait`, the trait doesn't need to be in scope
461 let inherent_trait = 475 let inherent_trait =
462 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); 476 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
@@ -489,23 +503,27 @@ fn iterate_trait_method_candidates<T>(
489 } 503 }
490 } 504 }
491 known_implemented = true; 505 known_implemented = true;
492 if let Some(result) = callback(&self_ty.value, *item) { 506 if callback(&self_ty.value, *item) {
493 return Some(result); 507 return true;
494 } 508 }
495 } 509 }
496 } 510 }
497 None 511 false
498} 512}
499 513
500fn iterate_inherent_methods<T>( 514fn iterate_inherent_methods(
501 self_ty: &Canonical<Ty>, 515 self_ty: &Canonical<Ty>,
502 db: &dyn HirDatabase, 516 db: &dyn HirDatabase,
503 name: Option<&Name>, 517 name: Option<&Name>,
504 receiver_ty: Option<&Canonical<Ty>>, 518 receiver_ty: Option<&Canonical<Ty>>,
505 krate: CrateId, 519 krate: CrateId,
506 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 520 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
507) -> Option<T> { 521) -> bool {
508 for krate in self_ty.value.def_crates(db, krate)? { 522 let def_crates = match self_ty.value.def_crates(db, krate) {
523 Some(k) => k,
524 None => return false,
525 };
526 for krate in def_crates {
509 let impls = db.impls_in_crate(krate); 527 let impls = db.impls_in_crate(krate);
510 528
511 for impl_def in impls.lookup_impl_defs(&self_ty.value) { 529 for impl_def in impls.lookup_impl_defs(&self_ty.value) {
@@ -521,13 +539,13 @@ fn iterate_inherent_methods<T>(
521 test_utils::mark::hit!(impl_self_type_match_without_receiver); 539 test_utils::mark::hit!(impl_self_type_match_without_receiver);
522 continue; 540 continue;
523 } 541 }
524 if let Some(result) = callback(&self_ty.value, item) { 542 if callback(&self_ty.value, item) {
525 return Some(result); 543 return true;
526 } 544 }
527 } 545 }
528 } 546 }
529 } 547 }
530 None 548 false
531} 549}
532 550
533/// Returns the self type for the index trait call. 551/// Returns the self type for the index trait call.
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 1fe05c70c..85ff26a36 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -67,8 +67,8 @@ fn type_at_pos_displayed(
67 panic!("Can't find expression") 67 panic!("Can't find expression")
68} 68}
69 69
70fn type_at(content: &str) -> String { 70fn type_at(ra_fixture: &str) -> String {
71 let (db, file_pos) = TestDB::with_position(content); 71 let (db, file_pos) = TestDB::with_position(ra_fixture);
72 type_at_pos(&db, file_pos) 72 type_at_pos(&db, file_pos)
73} 73}
74 74
@@ -164,13 +164,19 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
164 visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it)); 164 visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it));
165 defs.sort_by_key(|def| match def { 165 defs.sort_by_key(|def| match def {
166 DefWithBodyId::FunctionId(it) => { 166 DefWithBodyId::FunctionId(it) => {
167 it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() 167 let loc = it.lookup(&db);
168 let tree = db.item_tree(loc.id.file_id);
169 tree.source(&db, loc.id).syntax().text_range().start()
168 } 170 }
169 DefWithBodyId::ConstId(it) => { 171 DefWithBodyId::ConstId(it) => {
170 it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() 172 let loc = it.lookup(&db);
173 let tree = db.item_tree(loc.id.file_id);
174 tree.source(&db, loc.id).syntax().text_range().start()
171 } 175 }
172 DefWithBodyId::StaticId(it) => { 176 DefWithBodyId::StaticId(it) => {
173 it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() 177 let loc = it.lookup(&db);
178 let tree = db.item_tree(loc.id.file_id);
179 tree.source(&db, loc.id).syntax().text_range().start()
174 } 180 }
175 }); 181 });
176 for def in defs { 182 for def in defs {
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 8fa296137..f937426bd 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -271,6 +271,63 @@ fn test() {
271} 271}
272 272
273#[test] 273#[test]
274fn infer_pattern_match_string_literal() {
275 assert_snapshot!(
276 infer_with_mismatches(r#"
277fn test() {
278 let s: &str = "hello";
279 match s {
280 "hello" => {}
281 _ => {}
282 }
283}
284"#, true),
285 @r###"
286 10..98 '{ ... } }': ()
287 20..21 's': &str
288 30..37 '"hello"': &str
289 43..96 'match ... }': ()
290 49..50 's': &str
291 61..68 '"hello"': &str
292 61..68 '"hello"': &str
293 72..74 '{}': ()
294 83..84 '_': &str
295 88..90 '{}': ()
296 "###
297 );
298}
299
300#[test]
301fn infer_pattern_match_or() {
302 assert_snapshot!(
303 infer_with_mismatches(r#"
304fn test() {
305 let s: &str = "hello";
306 match s {
307 "hello" | "world" => {}
308 _ => {}
309 }
310}
311"#, true),
312 @r###"
313 10..108 '{ ... } }': ()
314 20..21 's': &str
315 30..37 '"hello"': &str
316 43..106 'match ... }': ()
317 49..50 's': &str
318 61..68 '"hello"': &str
319 61..68 '"hello"': &str
320 61..78 '"hello...world"': &str
321 71..78 '"world"': &str
322 71..78 '"world"': &str
323 82..84 '{}': ()
324 93..94 '_': &str
325 98..100 '{}': ()
326 "###
327 );
328}
329
330#[test]
274fn infer_pattern_match_arr() { 331fn infer_pattern_match_arr() {
275 assert_snapshot!( 332 assert_snapshot!(
276 infer(r#" 333 infer(r#"
@@ -570,3 +627,28 @@ fn test() {
570 "### 627 "###
571 ); 628 );
572} 629}
630
631#[test]
632fn slice_tail_pattern() {
633 assert_snapshot!(
634 infer(r#"
635fn foo(params: &[i32]) {
636 match params {
637 [head, tail @ ..] => {
638 }
639 }
640}
641"#),
642 @r###"
643 7..13 'params': &[i32]
644 23..92 '{ ... } }': ()
645 29..90 'match ... }': ()
646 35..41 'params': &[i32]
647 52..69 '[head,... @ ..]': [i32]
648 53..57 'head': &i32
649 59..68 'tail @ ..': &[i32]
650 66..68 '..': [i32]
651 73..84 '{ }': ()
652 "###
653 );
654}
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index eedaa27ba..aa37326df 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -500,6 +500,8 @@ fn foo(params: &[usize]) {
500 31..78 'match ... }': () 500 31..78 'match ... }': ()
501 37..43 'params': &[usize] 501 37..43 'params': &[usize]
502 54..66 '[ps @ .., _]': [usize] 502 54..66 '[ps @ .., _]': [usize]
503 55..62 'ps @ ..': &[usize]
504 60..62 '..': [usize]
503 64..65 '_': usize 505 64..65 '_': usize
504 70..72 '{}': () 506 70..72 '{}': ()
505 "### 507 "###