From 5d72b96988587699e0a1c62c08bd76d2a7fed100 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 24 Feb 2019 17:25:41 +0100 Subject: Implement support for type aliases --- crates/ra_hir/src/code_model_api.rs | 17 ++++++++++++ crates/ra_hir/src/db.rs | 5 +++- crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/ty/infer.rs | 4 ++- crates/ra_hir/src/ty/lower.rs | 32 ++++++++++++++++------ .../src/ty/snapshots/tests__infer_type_alias.snap | 23 ++++++++++++++++ crates/ra_hir/src/ty/tests.rs | 21 ++++++++++++++ crates/ra_hir/src/type_alias.rs | 10 +++++++ 8 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 crates/ra_hir/src/ty/snapshots/tests__infer_type_alias.snap create mode 100644 crates/ra_hir/src/type_alias.rs (limited to 'crates') diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 0779a08a9..29c08e34b 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -626,6 +626,23 @@ impl Type { let module_impls = db.impls_in_module(self.module(db)); ImplBlock::containing(module_impls, (*self).into()) } + + pub fn type_ref(self, db: &impl PersistentHirDatabase) -> Arc { + db.type_alias_ref(self) + } + + /// Builds a resolver for the type references in this type alias. + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + // take the outer scope... + let r = self + .impl_block(db) + .map(|ib| ib.resolver(db)) + .unwrap_or_else(|| self.module(db).resolver(db)); + // ...and add generic params, if present + let p = self.generic_params(db); + let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; + r + } } impl Docs for Type { diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index fc0ee068c..b8bd82f0c 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -15,7 +15,7 @@ use crate::{ adt::{StructData, EnumData}, impl_block::{ModuleImplBlocks, ImplSourceMap}, generics::{GenericParams, GenericDef}, - ids::SourceFileItemId, nameres::Namespace + ids::SourceFileItemId, nameres::Namespace, type_ref::TypeRef, code_model_api::Type }; #[salsa::query_group(PersistentHirDatabaseStorage)] @@ -77,6 +77,9 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef { #[salsa::invoke(crate::FnSignature::fn_signature_query)] fn fn_signature(&self, func: Function) -> Arc; + + #[salsa::invoke(crate::type_alias::type_alias_ref_query)] + fn type_alias_ref(&self, typ: Type) -> Arc; } #[salsa::query_group(HirDatabaseStorage)] diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index f2e0aae6c..78fa4952b 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -29,6 +29,7 @@ mod name; mod module_tree; mod nameres; mod adt; +mod type_alias; mod type_ref; mod ty; mod impl_block; diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 13080b5aa..29331bea5 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -477,7 +477,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = self.insert_type_vars(ty.apply_substs(substs)); (ty, Some(var.into())) } - TypableDef::Function(_) | TypableDef::Enum(_) => (Ty::Unknown, None), + TypableDef::Type(_) | TypableDef::Function(_) | TypableDef::Enum(_) => { + (Ty::Unknown, None) + } } } diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 63e13a30e..a11d964c8 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use crate::{ Function, Struct, StructField, Enum, EnumVariant, Path, Name, - ModuleDef, + ModuleDef, Type, HirDatabase, type_ref::TypeRef, name::KnownName, @@ -109,7 +109,7 @@ impl Ty { }; let ty = db.type_for_def(typable, Namespace::Types); let substs = Ty::substs_from_path(db, resolver, path, typable); - ty.apply_substs(substs) + ty.subst(&substs) } pub(super) fn substs_from_path_segment( @@ -124,6 +124,7 @@ impl Ty { TypableDef::Struct(s) => s.generic_params(db), TypableDef::Enum(e) => e.generic_params(db), TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db), + TypableDef::Type(t) => t.generic_params(db), }; let parent_param_count = def_generics.count_parent_params(); substs.extend((0..parent_param_count).map(|_| Ty::Unknown)); @@ -159,9 +160,10 @@ impl Ty { ) -> Substs { let last = path.segments.last().expect("path should have at least one segment"); let segment = match resolved { - TypableDef::Function(_) => last, - TypableDef::Struct(_) => last, - TypableDef::Enum(_) => last, + TypableDef::Function(_) + | TypableDef::Struct(_) + | TypableDef::Enum(_) + | TypableDef::Type(_) => last, TypableDef::EnumVariant(_) => { // the generic args for an enum variant may be either specified // on the segment referring to the enum, or on the segment @@ -194,11 +196,13 @@ pub(crate) fn type_for_def(db: &impl HirDatabase, def: TypableDef, ns: Namespace (TypableDef::Struct(s), Namespace::Values) => type_for_struct_constructor(db, s), (TypableDef::Enum(e), Namespace::Types) => type_for_enum(db, e), (TypableDef::EnumVariant(v), Namespace::Values) => type_for_enum_variant_constructor(db, v), + (TypableDef::Type(t), Namespace::Types) => type_for_type_alias(db, t), // 'error' cases: (TypableDef::Function(_), Namespace::Types) => Ty::Unknown, (TypableDef::Enum(_), Namespace::Values) => Ty::Unknown, (TypableDef::EnumVariant(_), Namespace::Types) => Ty::Unknown, + (TypableDef::Type(_), Namespace::Values) => Ty::Unknown, } } @@ -264,7 +268,7 @@ fn type_for_enum_variant_constructor(db: &impl HirDatabase, def: EnumVariant) -> .map(|(_, field)| Ty::from_hir(db, &resolver, &field.type_ref)) .collect::>(); let substs = make_substs(&generics); - let output = type_for_enum(db, def.parent_enum(db)).apply_substs(substs.clone()); + let output = type_for_enum(db, def.parent_enum(db)).subst(&substs); let sig = Arc::new(FnSig { input, output }); Ty::FnDef { def: def.into(), sig, name, substs } } @@ -298,14 +302,24 @@ fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty { } } +fn type_for_type_alias(db: &impl HirDatabase, t: Type) -> Ty { + let generics = t.generic_params(db); + let resolver = t.resolver(db); + let type_ref = t.type_ref(db); + let substs = make_substs(&generics); + let inner = Ty::from_hir(db, &resolver, &type_ref); + inner.subst(&substs) +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum TypableDef { Function(Function), Struct(Struct), Enum(Enum), EnumVariant(EnumVariant), + Type(Type), } -impl_froms!(TypableDef: Function, Struct, Enum, EnumVariant); +impl_froms!(TypableDef: Function, Struct, Enum, EnumVariant, Type); impl From for Option { fn from(def: ModuleDef) -> Option { @@ -314,11 +328,11 @@ impl From for Option { ModuleDef::Struct(s) => s.into(), ModuleDef::Enum(e) => e.into(), ModuleDef::EnumVariant(v) => v.into(), + ModuleDef::Type(t) => t.into(), ModuleDef::Const(_) | ModuleDef::Static(_) | ModuleDef::Module(_) - | ModuleDef::Trait(_) - | ModuleDef::Type(_) => return None, + | ModuleDef::Trait(_) => return None, }; Some(res) } diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_type_alias.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_type_alias.snap new file mode 100644 index 000000000..241c08353 --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_type_alias.snap @@ -0,0 +1,23 @@ +--- +created: "2019-02-24T16:13:47.561870283Z" +creator: insta@0.6.3 +source: crates/ra_hir/src/ty/tests.rs +expression: "&result" +--- +[117; 118) 'x': A +[125; 126) 'y': A<&str, u128> +[139; 140) 'z': A +[155; 212) '{ ...z.y; }': () +[161; 162) 'x': A +[161; 164) 'x.x': u32 +[170; 171) 'x': A +[170; 173) 'x.y': i128 +[179; 180) 'y': A<&str, u128> +[179; 182) 'y.x': &str +[188; 189) 'y': A<&str, u128> +[188; 191) 'y.y': u128 +[197; 198) 'z': A +[197; 200) 'z.x': u8 +[206; 207) 'z': A +[206; 209) 'z.y': i8 + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index d0da34677..490c087f9 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -740,6 +740,27 @@ fn test() { ); } +#[test] +fn infer_type_alias() { + check_inference( + "infer_type_alias", + r#" +struct A { x: X, y: Y }; +type Foo = A; +type Bar = A; +type Baz = A; +fn test(x: Foo, y: Bar<&str>, z: Baz) { + x.x; + x.y; + y.x; + y.y; + z.x; + z.y; +} +"#, + ) +} + #[test] fn no_panic_on_field_of_enum() { check_inference( diff --git a/crates/ra_hir/src/type_alias.rs b/crates/ra_hir/src/type_alias.rs new file mode 100644 index 000000000..ab9481708 --- /dev/null +++ b/crates/ra_hir/src/type_alias.rs @@ -0,0 +1,10 @@ +//! HIR for type aliases (i.e. the `type` keyword). + +use std::sync::Arc; + +use crate::{code_model_api::Type, db::PersistentHirDatabase, type_ref::TypeRef}; + +pub(crate) fn type_alias_ref_query(db: &impl PersistentHirDatabase, typ: Type) -> Arc { + let (_, node) = typ.source(db); + Arc::new(TypeRef::from_ast_opt(node.type_ref())) +} -- cgit v1.2.3 From c3c09795614f31f988edc9cb051ce024d1996d89 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 24 Feb 2019 17:29:07 +0100 Subject: Add test for recursive type aliases --- crates/ra_hir/src/ty/tests.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 490c087f9..642259225 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -761,6 +761,20 @@ fn test(x: Foo, y: Bar<&str>, z: Baz) { ) } +#[test] +#[should_panic] // we currently can't handle this +fn recursive_type_alias() { + check_inference( + "recursive_type_alias", + r#" +struct A {}; +type Foo = Foo; +type Bar = A; +fn test(x: Foo) {} +"#, + ) +} + #[test] fn no_panic_on_field_of_enum() { check_inference( -- cgit v1.2.3