diff options
author | Florian Diebold <[email protected]> | 2020-05-15 16:15:40 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-05-15 16:25:28 +0100 |
commit | 3f42b2e837c4672a0fbe953e14ae2fd3fe6fc3b6 (patch) | |
tree | 481c93f91eec65f98a82fa28a123140d5a227eea /crates | |
parent | d51c1f62178c383363a2d95e865131d9a7b969d0 (diff) |
Handle `Self` in values and patterns
I.e.
- `Self(x)` or `Self` in tuple/unit struct impls
- `Self::Variant(x)` or `Self::Variant` in enum impls
- the same in patterns
Fixes #4454.
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/source_analyzer.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_def/src/resolver.rs | 18 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 79 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/path.rs | 35 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/patterns.rs | 39 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/simple.rs | 44 |
6 files changed, 197 insertions, 19 deletions
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index c63d1b847..c862a4f48 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -417,6 +417,7 @@ pub(crate) fn resolve_hir_path( | |||
417 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | 417 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), |
418 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | 418 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), |
419 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | 419 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), |
420 | ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), | ||
420 | }; | 421 | }; |
421 | Some(res) | 422 | Some(res) |
422 | }); | 423 | }); |
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 717506358..15fdd9019 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs | |||
@@ -86,6 +86,7 @@ pub enum ResolveValueResult { | |||
86 | 86 | ||
87 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 87 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
88 | pub enum ValueNs { | 88 | pub enum ValueNs { |
89 | ImplSelf(ImplId), | ||
89 | LocalBinding(PatId), | 90 | LocalBinding(PatId), |
90 | FunctionId(FunctionId), | 91 | FunctionId(FunctionId), |
91 | ConstId(ConstId), | 92 | ConstId(ConstId), |
@@ -291,19 +292,26 @@ impl Resolver { | |||
291 | } | 292 | } |
292 | Scope::GenericParams { .. } => continue, | 293 | Scope::GenericParams { .. } => continue, |
293 | 294 | ||
294 | Scope::ImplDefScope(impl_) if n_segments > 1 => { | 295 | Scope::ImplDefScope(impl_) => { |
295 | if first_name == &name![Self] { | 296 | if first_name == &name![Self] { |
296 | let ty = TypeNs::SelfType(*impl_); | 297 | if n_segments > 1 { |
297 | return Some(ResolveValueResult::Partial(ty, 1)); | 298 | let ty = TypeNs::SelfType(*impl_); |
299 | return Some(ResolveValueResult::Partial(ty, 1)); | ||
300 | } else { | ||
301 | return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(*impl_))); | ||
302 | } | ||
298 | } | 303 | } |
299 | } | 304 | } |
300 | Scope::AdtScope(adt) if n_segments > 1 => { | 305 | Scope::AdtScope(adt) => { |
306 | if n_segments == 1 { | ||
307 | // bare `Self` doesn't work in the value namespace in a struct/enum definition | ||
308 | continue; | ||
309 | } | ||
301 | if first_name == &name![Self] { | 310 | if first_name == &name![Self] { |
302 | let ty = TypeNs::AdtSelfType(*adt); | 311 | let ty = TypeNs::AdtSelfType(*adt); |
303 | return Some(ResolveValueResult::Partial(ty, 1)); | 312 | return Some(ResolveValueResult::Partial(ty, 1)); |
304 | } | 313 | } |
305 | } | 314 | } |
306 | Scope::ImplDefScope(_) | Scope::AdtScope(_) => continue, | ||
307 | 315 | ||
308 | Scope::ModuleScope(m) => { | 316 | Scope::ModuleScope(m) => { |
309 | let (module_def, idx) = m.crate_def_map.resolve_path( | 317 | let (module_def, idx) = m.crate_def_map.resolve_path( |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index fb7c6cd8c..2876cb141 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -28,7 +28,8 @@ use hir_def::{ | |||
28 | path::{path, Path}, | 28 | path::{path, Path}, |
29 | resolver::{HasResolver, Resolver, TypeNs}, | 29 | resolver::{HasResolver, Resolver, TypeNs}, |
30 | type_ref::{Mutability, TypeRef}, | 30 | type_ref::{Mutability, TypeRef}, |
31 | AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, TraitId, TypeAliasId, VariantId, | 31 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, TraitId, TypeAliasId, |
32 | VariantId, | ||
32 | }; | 33 | }; |
33 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; | 34 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; |
34 | use ra_arena::map::ArenaMap; | 35 | use ra_arena::map::ArenaMap; |
@@ -438,43 +439,95 @@ impl<'a> InferenceContext<'a> { | |||
438 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); | 439 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); |
439 | // FIXME: this should resolve assoc items as well, see this example: | 440 | // FIXME: this should resolve assoc items as well, see this example: |
440 | // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 | 441 | // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 |
441 | return match resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path()) { | 442 | let (resolution, unresolved) = |
442 | Some(TypeNs::AdtId(AdtId::StructId(strukt))) => { | 443 | match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { |
444 | Some(it) => it, | ||
445 | None => return (Ty::Unknown, None), | ||
446 | }; | ||
447 | return match resolution { | ||
448 | TypeNs::AdtId(AdtId::StructId(strukt)) => { | ||
443 | let substs = Ty::substs_from_path(&ctx, path, strukt.into()); | 449 | let substs = Ty::substs_from_path(&ctx, path, strukt.into()); |
444 | let ty = self.db.ty(strukt.into()); | 450 | let ty = self.db.ty(strukt.into()); |
445 | let ty = self.insert_type_vars(ty.subst(&substs)); | 451 | let ty = self.insert_type_vars(ty.subst(&substs)); |
446 | (ty, Some(strukt.into())) | 452 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) |
447 | } | 453 | } |
448 | Some(TypeNs::EnumVariantId(var)) => { | 454 | TypeNs::EnumVariantId(var) => { |
449 | let substs = Ty::substs_from_path(&ctx, path, var.into()); | 455 | let substs = Ty::substs_from_path(&ctx, path, var.into()); |
450 | let ty = self.db.ty(var.parent.into()); | 456 | let ty = self.db.ty(var.parent.into()); |
451 | let ty = self.insert_type_vars(ty.subst(&substs)); | 457 | let ty = self.insert_type_vars(ty.subst(&substs)); |
452 | (ty, Some(var.into())) | 458 | forbid_unresolved_segments((ty, Some(var.into())), unresolved) |
453 | } | 459 | } |
454 | Some(TypeNs::SelfType(impl_id)) => { | 460 | TypeNs::SelfType(impl_id) => { |
455 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | 461 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); |
456 | let substs = Substs::type_params_for_generics(&generics); | 462 | let substs = Substs::type_params_for_generics(&generics); |
457 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | 463 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); |
458 | let variant = ty_variant(&ty); | 464 | match unresolved { |
459 | (ty, variant) | 465 | None => { |
466 | let variant = ty_variant(&ty); | ||
467 | (ty, variant) | ||
468 | } | ||
469 | Some(1) => { | ||
470 | let segment = path.mod_path().segments.last().unwrap(); | ||
471 | // this could be an enum variant or associated type | ||
472 | if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { | ||
473 | let enum_data = self.db.enum_data(enum_id); | ||
474 | if let Some(local_id) = enum_data.variant(segment) { | ||
475 | let variant = EnumVariantId { parent: enum_id, local_id }; | ||
476 | return (ty, Some(variant.into())); | ||
477 | } | ||
478 | } | ||
479 | // FIXME potentially resolve assoc type | ||
480 | (Ty::Unknown, None) | ||
481 | } | ||
482 | Some(_) => { | ||
483 | // FIXME diagnostic | ||
484 | (Ty::Unknown, None) | ||
485 | } | ||
486 | } | ||
460 | } | 487 | } |
461 | Some(TypeNs::TypeAliasId(it)) => { | 488 | TypeNs::TypeAliasId(it) => { |
462 | let substs = Substs::build_for_def(self.db, it) | 489 | let substs = Substs::build_for_def(self.db, it) |
463 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) | 490 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) |
464 | .build(); | 491 | .build(); |
465 | let ty = self.db.ty(it.into()).subst(&substs); | 492 | let ty = self.db.ty(it.into()).subst(&substs); |
466 | let variant = ty_variant(&ty); | 493 | let variant = ty_variant(&ty); |
467 | (ty, variant) | 494 | forbid_unresolved_segments((ty, variant), unresolved) |
495 | } | ||
496 | TypeNs::AdtSelfType(_) => { | ||
497 | // FIXME this could happen in array size expressions, once we're checking them | ||
498 | (Ty::Unknown, None) | ||
499 | } | ||
500 | TypeNs::GenericParam(_) => { | ||
501 | // FIXME potentially resolve assoc type | ||
502 | (Ty::Unknown, None) | ||
503 | } | ||
504 | TypeNs::AdtId(AdtId::EnumId(_)) | ||
505 | | TypeNs::AdtId(AdtId::UnionId(_)) | ||
506 | | TypeNs::BuiltinType(_) | ||
507 | | TypeNs::TraitId(_) => { | ||
508 | // FIXME diagnostic | ||
509 | (Ty::Unknown, None) | ||
468 | } | 510 | } |
469 | Some(_) | None => (Ty::Unknown, None), | ||
470 | }; | 511 | }; |
471 | 512 | ||
513 | fn forbid_unresolved_segments( | ||
514 | result: (Ty, Option<VariantId>), | ||
515 | unresolved: Option<usize>, | ||
516 | ) -> (Ty, Option<VariantId>) { | ||
517 | if unresolved.is_none() { | ||
518 | result | ||
519 | } else { | ||
520 | // FIXME diagnostic | ||
521 | (Ty::Unknown, None) | ||
522 | } | ||
523 | } | ||
524 | |||
472 | fn ty_variant(ty: &Ty) -> Option<VariantId> { | 525 | fn ty_variant(ty: &Ty) -> Option<VariantId> { |
473 | ty.as_adt().and_then(|(adt_id, _)| match adt_id { | 526 | ty.as_adt().and_then(|(adt_id, _)| match adt_id { |
474 | AdtId::StructId(s) => Some(VariantId::StructId(s)), | 527 | AdtId::StructId(s) => Some(VariantId::StructId(s)), |
475 | AdtId::UnionId(u) => Some(VariantId::UnionId(u)), | 528 | AdtId::UnionId(u) => Some(VariantId::UnionId(u)), |
476 | AdtId::EnumId(_) => { | 529 | AdtId::EnumId(_) => { |
477 | // Error E0071, expected struct, variant or union type, found enum `Foo` | 530 | // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` |
478 | None | 531 | None |
479 | } | 532 | } |
480 | }) | 533 | }) |
diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs index 2b6bc0f79..1c2e56fb0 100644 --- a/crates/ra_hir_ty/src/infer/path.rs +++ b/crates/ra_hir_ty/src/infer/path.rs | |||
@@ -5,7 +5,7 @@ use std::iter; | |||
5 | use hir_def::{ | 5 | use hir_def::{ |
6 | path::{Path, PathSegment}, | 6 | path::{Path, PathSegment}, |
7 | resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, | 7 | resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, |
8 | AssocContainerId, AssocItemId, Lookup, | 8 | AdtId, AssocContainerId, AssocItemId, EnumVariantId, Lookup, |
9 | }; | 9 | }; |
10 | use hir_expand::name::Name; | 10 | use hir_expand::name::Name; |
11 | 11 | ||
@@ -77,6 +77,18 @@ impl<'a> InferenceContext<'a> { | |||
77 | 77 | ||
78 | it.into() | 78 | it.into() |
79 | } | 79 | } |
80 | ValueNs::ImplSelf(impl_id) => { | ||
81 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | ||
82 | let substs = Substs::type_params_for_generics(&generics); | ||
83 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | ||
84 | if let Some((AdtId::StructId(struct_id), _)) = ty.as_adt() { | ||
85 | let ty = self.db.value_ty(struct_id.into()).subst(&substs); | ||
86 | return Some(ty); | ||
87 | } else { | ||
88 | // FIXME: diagnostic, invalid Self reference | ||
89 | return None; | ||
90 | } | ||
91 | } | ||
80 | }; | 92 | }; |
81 | 93 | ||
82 | let ty = self.db.value_ty(typable); | 94 | let ty = self.db.value_ty(typable); |
@@ -199,6 +211,10 @@ impl<'a> InferenceContext<'a> { | |||
199 | return None; | 211 | return None; |
200 | } | 212 | } |
201 | 213 | ||
214 | if let Some(result) = self.resolve_enum_variant_on_ty(&ty, name, id) { | ||
215 | return Some(result); | ||
216 | } | ||
217 | |||
202 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); | 218 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); |
203 | let krate = self.resolver.krate()?; | 219 | let krate = self.resolver.krate()?; |
204 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); | 220 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); |
@@ -250,4 +266,21 @@ impl<'a> InferenceContext<'a> { | |||
250 | }, | 266 | }, |
251 | ) | 267 | ) |
252 | } | 268 | } |
269 | |||
270 | fn resolve_enum_variant_on_ty( | ||
271 | &mut self, | ||
272 | ty: &Ty, | ||
273 | name: &Name, | ||
274 | id: ExprOrPatId, | ||
275 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
276 | let (enum_id, subst) = match ty.as_adt() { | ||
277 | Some((AdtId::EnumId(e), subst)) => (e, subst), | ||
278 | _ => return None, | ||
279 | }; | ||
280 | let enum_data = self.db.enum_data(enum_id); | ||
281 | let local_id = enum_data.variant(name)?; | ||
282 | let variant = EnumVariantId { parent: enum_id, local_id }; | ||
283 | self.write_variant_resolution(id, variant.into()); | ||
284 | Some((ValueNs::EnumVariantId(variant), Some(subst.clone()))) | ||
285 | } | ||
253 | } | 286 | } |
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index af291092d..d83ff5e0e 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs | |||
@@ -369,6 +369,45 @@ fn test() { | |||
369 | } | 369 | } |
370 | 370 | ||
371 | #[test] | 371 | #[test] |
372 | fn enum_variant_through_self_in_pattern() { | ||
373 | assert_snapshot!( | ||
374 | infer(r#" | ||
375 | enum E { | ||
376 | A { x: usize }, | ||
377 | B(usize), | ||
378 | C | ||
379 | } | ||
380 | |||
381 | impl E { | ||
382 | fn test() { | ||
383 | match (loop {}) { | ||
384 | Self::A { x } => { x; }, | ||
385 | Self::B(x) => { x; }, | ||
386 | Self::C => {}, | ||
387 | }; | ||
388 | } | ||
389 | } | ||
390 | "#), | ||
391 | @r###" | ||
392 | 76..218 '{ ... }': () | ||
393 | 86..211 'match ... }': () | ||
394 | 93..100 'loop {}': ! | ||
395 | 98..100 '{}': () | ||
396 | 116..129 'Self::A { x }': E | ||
397 | 126..127 'x': usize | ||
398 | 133..139 '{ x; }': () | ||
399 | 135..136 'x': usize | ||
400 | 153..163 'Self::B(x)': E | ||
401 | 161..162 'x': usize | ||
402 | 167..173 '{ x; }': () | ||
403 | 169..170 'x': usize | ||
404 | 187..194 'Self::C': E | ||
405 | 198..200 '{}': () | ||
406 | "### | ||
407 | ); | ||
408 | } | ||
409 | |||
410 | #[test] | ||
372 | fn infer_generics_in_patterns() { | 411 | fn infer_generics_in_patterns() { |
373 | assert_snapshot!( | 412 | assert_snapshot!( |
374 | infer(r#" | 413 | infer(r#" |
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 322838f02..72122c070 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -576,6 +576,50 @@ impl S { | |||
576 | } | 576 | } |
577 | 577 | ||
578 | #[test] | 578 | #[test] |
579 | fn infer_self_as_path() { | ||
580 | assert_snapshot!( | ||
581 | infer(r#" | ||
582 | struct S1; | ||
583 | struct S2(isize); | ||
584 | enum E { | ||
585 | V1, | ||
586 | V2(u32), | ||
587 | } | ||
588 | |||
589 | impl S1 { | ||
590 | fn test() { | ||
591 | Self; | ||
592 | } | ||
593 | } | ||
594 | impl S2 { | ||
595 | fn test() { | ||
596 | Self(1); | ||
597 | } | ||
598 | } | ||
599 | impl E { | ||
600 | fn test() { | ||
601 | Self::V1; | ||
602 | Self::V2(1); | ||
603 | } | ||
604 | } | ||
605 | "#), | ||
606 | @r###" | ||
607 | 87..108 '{ ... }': () | ||
608 | 97..101 'Self': S1 | ||
609 | 135..159 '{ ... }': () | ||
610 | 145..149 'Self': S2(isize) -> S2 | ||
611 | 145..152 'Self(1)': S2 | ||
612 | 150..151 '1': isize | ||
613 | 185..231 '{ ... }': () | ||
614 | 195..203 'Self::V1': E | ||
615 | 213..221 'Self::V2': V2(u32) -> E | ||
616 | 213..224 'Self::V2(1)': E | ||
617 | 222..223 '1': u32 | ||
618 | "### | ||
619 | ); | ||
620 | } | ||
621 | |||
622 | #[test] | ||
579 | fn infer_binary_op() { | 623 | fn infer_binary_op() { |
580 | assert_snapshot!( | 624 | assert_snapshot!( |
581 | infer(r#" | 625 | infer(r#" |