aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty.rs')
-rw-r--r--crates/ra_hir/src/ty.rs102
1 files changed, 97 insertions, 5 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)]
157pub struct Substs(Arc<[Ty]>); 158pub struct Substs(Arc<[Ty]>);
158 159
160impl 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 {
271impl Ty { 278impl 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.)