From cc4562ab6ecfced33f5d7a2e428c8caca8f9c3f1 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 13 Jan 2019 21:00:31 +0100 Subject: Collect generic args in type paths E.g. `let x: A` is handled correctly. --- crates/ra_hir/src/ty.rs | 102 ++++++++++++++++++++- .../ra_hir/src/ty/tests/data/struct_generics.txt | 6 +- 2 files changed, 100 insertions(+), 8 deletions(-) (limited to 'crates') 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::{ name::KnownName, expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, generics::Generics, + path::GenericArg, }; /// The ID of a type variable. @@ -156,6 +157,12 @@ impl Expectation { #[derive(Clone, PartialEq, Eq, Debug)] pub struct Substs(Arc<[Ty]>); +impl Substs { + pub fn empty() -> Substs { + Substs(Arc::new([])) + } +} + /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). /// /// This should be cheap to clone. @@ -271,6 +278,9 @@ pub struct FnSig { impl Ty { pub(crate) fn from_hir( db: &impl HirDatabase, + // TODO: the next three parameters basically describe the scope for name + // resolution; this should be refactored into something like a general + // resolver architecture module: &Module, impl_block: Option<&ImplBlock>, generics: &Generics, @@ -371,12 +381,79 @@ impl Ty { } // Resolve in module (in type namespace) - let resolved = if let Some(r) = module.resolve_path(db, path).take_types() { - r - } else { - return Ty::Unknown; + let resolved = match module.resolve_path(db, path).take_types() { + Some(r) => r, + None => return Ty::Unknown, + }; + let ty = db.type_for_def(resolved); + let substs = Ty::substs_from_path(db, module, impl_block, generics, path, resolved); + ty.apply_substs(substs) + } + + /// Collect generic arguments from a path into a `Substs`. See also + /// `create_substs_for_ast_path` and `def_to_ty` in rustc. + fn substs_from_path( + db: &impl HirDatabase, + // the scope of the segment... + module: &Module, + impl_block: Option<&ImplBlock>, + outer_generics: &Generics, + path: &Path, + resolved: DefId, + ) -> Substs { + let mut substs = Vec::new(); + let def = resolved.resolve(db); + let last = path + .segments + .last() + .expect("path should have at least one segment"); + let (def_generics, segment) = match def { + Def::Struct(s) => (s.generics(db), last), + Def::Enum(e) => (e.generics(db), last), + Def::Function(f) => (f.generics(db), last), + Def::Trait(t) => (t.generics(db), last), + Def::EnumVariant(ev) => { + // the generic args for an enum variant may be either specified + // on the segment referring to the enum, or on the segment + // referring to the variant. So `Option::::None` and + // `Option::None::` are both allowed (though the former is + // preferred). See also `def_ids_for_path_segments` in rustc. + let len = path.segments.len(); + let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() { + // Option::::None + &path.segments[len - 2] + } else { + // Option::None:: + last + }; + (ev.parent_enum(db).generics(db), segment) + } + _ => return Substs::empty(), }; - db.type_for_def(resolved) + // substs_from_path + if let Some(generic_args) = &segment.args_and_bindings { + // if args are provided, it should be all of them, but we can't rely on that + let param_count = def_generics.params.len(); + for arg in generic_args.args.iter().take(param_count) { + match arg { + GenericArg::Type(type_ref) => { + let ty = Ty::from_hir(db, module, impl_block, outer_generics, type_ref); + substs.push(ty); + } + } + } + } + // add placeholders for args that were not provided + // TODO: handle defaults + for _ in segment + .args_and_bindings + .as_ref() + .map(|ga| ga.args.len()) + .unwrap_or(0)..def_generics.params.len() + { + substs.push(Ty::Unknown); + } + Substs(substs.into()) } pub fn unit() -> Self { @@ -432,6 +509,21 @@ impl Ty { } } + /// If this is a type with type parameters (an ADT or function), replaces + /// the `Substs` for these type parameters with the given ones. (So e.g. if + /// `self` is `Option<_>` and the substs contain `u32`, we'll have + /// `Option` afterwards.) + pub fn apply_substs(self, substs: Substs) -> Ty { + match self { + Ty::Adt { def_id, name, .. } => Ty::Adt { + def_id, + name, + substs, + }, + _ => self, + } + } + /// Replaces type parameters in this type using the given `Substs`. (So e.g. /// if `self` is `&[T]`, where type parameter T has index 0, and the /// `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 @@ -[36; 38) 'a1': A<[unknown]> +[36; 38) 'a1': A [48; 49) 'i': i32 [56; 147) '{ ...3.x; }': () -[62; 64) 'a1': A<[unknown]> -[62; 66) 'a1.x': [unknown] +[62; 64) 'a1': A +[62; 66) 'a1.x': u32 [76; 78) 'a2': A [81; 91) 'A { x: i }': A [88; 89) 'i': i32 -- cgit v1.2.3