diff options
-rw-r--r-- | crates/ra_hir/src/ty.rs | 102 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/struct_generics.txt | 6 |
2 files changed, 100 insertions, 8 deletions
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 8e93a4457..fcde918fb 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -38,6 +38,7 @@ use crate::{ | |||
38 | name::KnownName, | 38 | name::KnownName, |
39 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, | 39 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, |
40 | generics::Generics, | 40 | generics::Generics, |
41 | path::GenericArg, | ||
41 | }; | 42 | }; |
42 | 43 | ||
43 | /// The ID of a type variable. | 44 | /// The ID of a type variable. |
@@ -156,6 +157,12 @@ impl Expectation { | |||
156 | #[derive(Clone, PartialEq, Eq, Debug)] | 157 | #[derive(Clone, PartialEq, Eq, Debug)] |
157 | pub struct Substs(Arc<[Ty]>); | 158 | pub struct Substs(Arc<[Ty]>); |
158 | 159 | ||
160 | impl Substs { | ||
161 | pub fn empty() -> Substs { | ||
162 | Substs(Arc::new([])) | ||
163 | } | ||
164 | } | ||
165 | |||
159 | /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). | 166 | /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). |
160 | /// | 167 | /// |
161 | /// This should be cheap to clone. | 168 | /// This should be cheap to clone. |
@@ -271,6 +278,9 @@ pub struct FnSig { | |||
271 | impl Ty { | 278 | impl Ty { |
272 | pub(crate) fn from_hir( | 279 | pub(crate) fn from_hir( |
273 | db: &impl HirDatabase, | 280 | db: &impl HirDatabase, |
281 | // TODO: the next three parameters basically describe the scope for name | ||
282 | // resolution; this should be refactored into something like a general | ||
283 | // resolver architecture | ||
274 | module: &Module, | 284 | module: &Module, |
275 | impl_block: Option<&ImplBlock>, | 285 | impl_block: Option<&ImplBlock>, |
276 | generics: &Generics, | 286 | generics: &Generics, |
@@ -371,12 +381,79 @@ impl Ty { | |||
371 | } | 381 | } |
372 | 382 | ||
373 | // Resolve in module (in type namespace) | 383 | // Resolve in module (in type namespace) |
374 | let resolved = if let Some(r) = module.resolve_path(db, path).take_types() { | 384 | let resolved = match module.resolve_path(db, path).take_types() { |
375 | r | 385 | Some(r) => r, |
376 | } else { | 386 | None => return Ty::Unknown, |
377 | return Ty::Unknown; | 387 | }; |
388 | let ty = db.type_for_def(resolved); | ||
389 | let substs = Ty::substs_from_path(db, module, impl_block, generics, path, resolved); | ||
390 | ty.apply_substs(substs) | ||
391 | } | ||
392 | |||
393 | /// Collect generic arguments from a path into a `Substs`. See also | ||
394 | /// `create_substs_for_ast_path` and `def_to_ty` in rustc. | ||
395 | fn substs_from_path( | ||
396 | db: &impl HirDatabase, | ||
397 | // the scope of the segment... | ||
398 | module: &Module, | ||
399 | impl_block: Option<&ImplBlock>, | ||
400 | outer_generics: &Generics, | ||
401 | path: &Path, | ||
402 | resolved: DefId, | ||
403 | ) -> Substs { | ||
404 | let mut substs = Vec::new(); | ||
405 | let def = resolved.resolve(db); | ||
406 | let last = path | ||
407 | .segments | ||
408 | .last() | ||
409 | .expect("path should have at least one segment"); | ||
410 | let (def_generics, segment) = match def { | ||
411 | Def::Struct(s) => (s.generics(db), last), | ||
412 | Def::Enum(e) => (e.generics(db), last), | ||
413 | Def::Function(f) => (f.generics(db), last), | ||
414 | Def::Trait(t) => (t.generics(db), last), | ||
415 | Def::EnumVariant(ev) => { | ||
416 | // the generic args for an enum variant may be either specified | ||
417 | // on the segment referring to the enum, or on the segment | ||
418 | // referring to the variant. So `Option::<T>::None` and | ||
419 | // `Option::None::<T>` are both allowed (though the former is | ||
420 | // preferred). See also `def_ids_for_path_segments` in rustc. | ||
421 | let len = path.segments.len(); | ||
422 | let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() { | ||
423 | // Option::<T>::None | ||
424 | &path.segments[len - 2] | ||
425 | } else { | ||
426 | // Option::None::<T> | ||
427 | last | ||
428 | }; | ||
429 | (ev.parent_enum(db).generics(db), segment) | ||
430 | } | ||
431 | _ => return Substs::empty(), | ||
378 | }; | 432 | }; |
379 | db.type_for_def(resolved) | 433 | // substs_from_path |
434 | if let Some(generic_args) = &segment.args_and_bindings { | ||
435 | // if args are provided, it should be all of them, but we can't rely on that | ||
436 | let param_count = def_generics.params.len(); | ||
437 | for arg in generic_args.args.iter().take(param_count) { | ||
438 | match arg { | ||
439 | GenericArg::Type(type_ref) => { | ||
440 | let ty = Ty::from_hir(db, module, impl_block, outer_generics, type_ref); | ||
441 | substs.push(ty); | ||
442 | } | ||
443 | } | ||
444 | } | ||
445 | } | ||
446 | // add placeholders for args that were not provided | ||
447 | // TODO: handle defaults | ||
448 | for _ in segment | ||
449 | .args_and_bindings | ||
450 | .as_ref() | ||
451 | .map(|ga| ga.args.len()) | ||
452 | .unwrap_or(0)..def_generics.params.len() | ||
453 | { | ||
454 | substs.push(Ty::Unknown); | ||
455 | } | ||
456 | Substs(substs.into()) | ||
380 | } | 457 | } |
381 | 458 | ||
382 | pub fn unit() -> Self { | 459 | pub fn unit() -> Self { |
@@ -432,6 +509,21 @@ impl Ty { | |||
432 | } | 509 | } |
433 | } | 510 | } |
434 | 511 | ||
512 | /// If this is a type with type parameters (an ADT or function), replaces | ||
513 | /// the `Substs` for these type parameters with the given ones. (So e.g. if | ||
514 | /// `self` is `Option<_>` and the substs contain `u32`, we'll have | ||
515 | /// `Option<u32>` afterwards.) | ||
516 | pub fn apply_substs(self, substs: Substs) -> Ty { | ||
517 | match self { | ||
518 | Ty::Adt { def_id, name, .. } => Ty::Adt { | ||
519 | def_id, | ||
520 | name, | ||
521 | substs, | ||
522 | }, | ||
523 | _ => self, | ||
524 | } | ||
525 | } | ||
526 | |||
435 | /// Replaces type parameters in this type using the given `Substs`. (So e.g. | 527 | /// Replaces type parameters in this type using the given `Substs`. (So e.g. |
436 | /// if `self` is `&[T]`, where type parameter T has index 0, and the | 528 | /// if `self` is `&[T]`, where type parameter T has index 0, and the |
437 | /// `Substs` contain `u32` at index 0, we'll have `&[u32]` afterwards.) | 529 | /// `Substs` contain `u32` at index 0, we'll have `&[u32]` afterwards.) |
diff --git a/crates/ra_hir/src/ty/tests/data/struct_generics.txt b/crates/ra_hir/src/ty/tests/data/struct_generics.txt index 88cd2b409..c6be2a5f5 100644 --- a/crates/ra_hir/src/ty/tests/data/struct_generics.txt +++ b/crates/ra_hir/src/ty/tests/data/struct_generics.txt | |||
@@ -1,8 +1,8 @@ | |||
1 | [36; 38) 'a1': A<[unknown]> | 1 | [36; 38) 'a1': A<u32> |
2 | [48; 49) 'i': i32 | 2 | [48; 49) 'i': i32 |
3 | [56; 147) '{ ...3.x; }': () | 3 | [56; 147) '{ ...3.x; }': () |
4 | [62; 64) 'a1': A<[unknown]> | 4 | [62; 64) 'a1': A<u32> |
5 | [62; 66) 'a1.x': [unknown] | 5 | [62; 66) 'a1.x': u32 |
6 | [76; 78) 'a2': A<i32> | 6 | [76; 78) 'a2': A<i32> |
7 | [81; 91) 'A { x: i }': A<i32> | 7 | [81; 91) 'A { x: i }': A<i32> |
8 | [88; 89) 'i': i32 | 8 | [88; 89) 'i': i32 |