From e1a6e38767c1e47e5e88a97a9ef5b4547390803c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 Nov 2019 12:25:02 +0300 Subject: Move Generics to hir_def --- crates/ra_hir_def/src/generics.rs | 163 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 crates/ra_hir_def/src/generics.rs (limited to 'crates/ra_hir_def/src/generics.rs') diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs new file mode 100644 index 000000000..4adfc16bb --- /dev/null +++ b/crates/ra_hir_def/src/generics.rs @@ -0,0 +1,163 @@ +//! Many kinds of items or constructs can have generic parameters: functions, +//! structs, impls, traits, etc. This module provides a common HIR for these +//! generic parameters. See also the `Generics` type and the `generics_of` query +//! in rustc. +use std::sync::Arc; + +use hir_expand::name::{self, AsName, Name}; + +use ra_syntax::ast::{self, NameOwner, TypeBoundsOwner, TypeParamsOwner}; + +use crate::{ + db::DefDatabase2, + type_ref::{TypeBound, TypeRef}, + AdtId, AstItemDef, GenericDefId, +}; + +/// Data about a generic parameter (to a function, struct, impl, ...). +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct GenericParam { + // FIXME: give generic params proper IDs + pub idx: u32, + pub name: Name, + pub default: Option, +} + +/// Data about the generic parameters of a function, struct, impl, etc. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct GenericParams { + pub def: GenericDefId, + pub parent_params: Option>, + pub params: Vec, + pub where_predicates: Vec, +} + +/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined +/// where clauses like `where T: Foo + Bar` are turned into multiple of these. +/// It might still result in multiple actual predicates though, because of +/// associated type bindings like `Iterator`. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct WherePredicate { + pub type_ref: TypeRef, + pub bound: TypeBound, +} + +impl GenericParams { + pub fn new( + db: &impl DefDatabase2, + def: GenericDefId, + parent_params: Option>, + ) -> GenericParams { + let mut generics = + GenericParams { def, params: Vec::new(), parent_params, where_predicates: Vec::new() }; + let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; + // FIXME: add `: Sized` bound for everything except for `Self` in traits + match def { + GenericDefId::FunctionId(it) => generics.fill(&it.source(db).value, start), + GenericDefId::AdtId(AdtId::StructId(it)) => { + generics.fill(&it.0.source(db).value, start) + } + GenericDefId::AdtId(AdtId::UnionId(it)) => generics.fill(&it.0.source(db).value, start), + GenericDefId::AdtId(AdtId::EnumId(it)) => generics.fill(&it.source(db).value, start), + GenericDefId::TraitId(it) => { + // traits get the Self type as an implicit first type parameter + generics.params.push(GenericParam { + idx: start, + name: name::SELF_TYPE, + default: None, + }); + generics.fill(&it.source(db).value, start + 1); + // add super traits as bounds on Self + // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar + let self_param = TypeRef::Path(name::SELF_TYPE.into()); + generics.fill_bounds(&it.source(db).value, self_param); + } + GenericDefId::TypeAliasId(it) => generics.fill(&it.source(db).value, start), + // Note that we don't add `Self` here: in `impl`s, `Self` is not a + // type-parameter, but rather is a type-alias for impl's target + // type, so this is handled by the resolver. + GenericDefId::ImplId(it) => generics.fill(&it.source(db).value, start), + GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {} + } + + generics + } + + fn fill(&mut self, node: &impl TypeParamsOwner, start: u32) { + if let Some(params) = node.type_param_list() { + self.fill_params(params, start) + } + if let Some(where_clause) = node.where_clause() { + self.fill_where_predicates(where_clause); + } + } + + fn fill_bounds(&mut self, node: &impl ast::TypeBoundsOwner, type_ref: TypeRef) { + for bound in + node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) + { + self.add_where_predicate_from_bound(bound, type_ref.clone()); + } + } + + fn fill_params(&mut self, params: ast::TypeParamList, start: u32) { + for (idx, type_param) in params.type_params().enumerate() { + let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); + // FIXME: Use `Path::from_src` + let default = type_param.default_type().map(TypeRef::from_ast); + let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default }; + self.params.push(param); + + let type_ref = TypeRef::Path(name.into()); + self.fill_bounds(&type_param, type_ref); + } + } + + fn fill_where_predicates(&mut self, where_clause: ast::WhereClause) { + for pred in where_clause.predicates() { + let type_ref = match pred.type_ref() { + Some(type_ref) => type_ref, + None => continue, + }; + let type_ref = TypeRef::from_ast(type_ref); + for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { + self.add_where_predicate_from_bound(bound, type_ref.clone()); + } + } + } + + fn add_where_predicate_from_bound(&mut self, bound: ast::TypeBound, type_ref: TypeRef) { + if bound.has_question_mark() { + // FIXME: remove this bound + return; + } + let bound = TypeBound::from_ast(bound); + self.where_predicates.push(WherePredicate { type_ref, bound }); + } + + pub fn find_by_name(&self, name: &Name) -> Option<&GenericParam> { + self.params.iter().find(|p| &p.name == name) + } + + pub fn count_parent_params(&self) -> usize { + self.parent_params.as_ref().map(|p| p.count_params_including_parent()).unwrap_or(0) + } + + pub fn count_params_including_parent(&self) -> usize { + let parent_count = self.count_parent_params(); + parent_count + self.params.len() + } + + fn for_each_param<'a>(&'a self, f: &mut impl FnMut(&'a GenericParam)) { + if let Some(parent) = &self.parent_params { + parent.for_each_param(f); + } + self.params.iter().for_each(f); + } + + pub fn params_including_parent(&self) -> Vec<&GenericParam> { + let mut vec = Vec::with_capacity(self.count_params_including_parent()); + self.for_each_param(&mut |p| vec.push(p)); + vec + } +} -- cgit v1.2.3 From cebeedc66fc40097eae20bf1767a285d00269966 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 Nov 2019 16:03:59 +0300 Subject: Next gen IDs for functions The current system with AstIds has two primaraly drawbacks: * It is possible to manufacture IDs out of thin air. For example, it's possible to create IDs for items which are not considered in CrateDefMap due to cfg. Or it is possible to mixup structs and unions, because they share ID space. * Getting the ID of a parent requires a secondary index. Instead, the plan is to pursue the more traditional approach, where each items stores the id of the parent declaration. This makes `FromSource` more awkward, but also more correct: now, to get from an AST to HIR, we first do this recursively for the parent item, and the just search the children of the parent for the matching def --- crates/ra_hir_def/src/generics.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_hir_def/src/generics.rs') diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs index 4adfc16bb..11dd2a948 100644 --- a/crates/ra_hir_def/src/generics.rs +++ b/crates/ra_hir_def/src/generics.rs @@ -11,7 +11,7 @@ use ra_syntax::ast::{self, NameOwner, TypeBoundsOwner, TypeParamsOwner}; use crate::{ db::DefDatabase2, type_ref::{TypeBound, TypeRef}, - AdtId, AstItemDef, GenericDefId, + AdtId, AstItemDef, GenericDefId, HasSource, Lookup, }; /// Data about a generic parameter (to a function, struct, impl, ...). @@ -53,7 +53,7 @@ impl GenericParams { let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; // FIXME: add `: Sized` bound for everything except for `Self` in traits match def { - GenericDefId::FunctionId(it) => generics.fill(&it.source(db).value, start), + GenericDefId::FunctionId(it) => generics.fill(&it.lookup(db).source(db).value, start), GenericDefId::AdtId(AdtId::StructId(it)) => { generics.fill(&it.0.source(db).value, start) } -- cgit v1.2.3 From 64c21ed19594b323e72605ba8c5dd4c6eee433f6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 Nov 2019 17:39:58 +0300 Subject: Switch type aliases to new sources --- crates/ra_hir_def/src/generics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_hir_def/src/generics.rs') diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs index 11dd2a948..17a5d5f43 100644 --- a/crates/ra_hir_def/src/generics.rs +++ b/crates/ra_hir_def/src/generics.rs @@ -72,7 +72,7 @@ impl GenericParams { let self_param = TypeRef::Path(name::SELF_TYPE.into()); generics.fill_bounds(&it.source(db).value, self_param); } - GenericDefId::TypeAliasId(it) => generics.fill(&it.source(db).value, start), + GenericDefId::TypeAliasId(it) => generics.fill(&it.lookup(db).source(db).value, start), // Note that we don't add `Self` here: in `impl`s, `Self` is not a // type-parameter, but rather is a type-alias for impl's target // type, so this is handled by the resolver. -- cgit v1.2.3 From cb642fc578100c0945088accb85acb8f03d2e1fd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 Nov 2019 20:33:18 +0300 Subject: Move generic_params query to HIR --- crates/ra_hir_def/src/generics.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'crates/ra_hir_def/src/generics.rs') diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs index 17a5d5f43..f794eefbc 100644 --- a/crates/ra_hir_def/src/generics.rs +++ b/crates/ra_hir_def/src/generics.rs @@ -5,13 +5,12 @@ use std::sync::Arc; use hir_expand::name::{self, AsName, Name}; - use ra_syntax::ast::{self, NameOwner, TypeBoundsOwner, TypeParamsOwner}; use crate::{ db::DefDatabase2, type_ref::{TypeBound, TypeRef}, - AdtId, AstItemDef, GenericDefId, HasSource, Lookup, + AdtId, AstItemDef, ContainerId, GenericDefId, HasSource, Lookup, }; /// Data about a generic parameter (to a function, struct, impl, ...). @@ -43,7 +42,15 @@ pub struct WherePredicate { } impl GenericParams { - pub fn new( + pub(crate) fn generic_params_query( + db: &impl DefDatabase2, + def: GenericDefId, + ) -> Arc { + let parent_generics = parent_generic_def(db, def).map(|it| db.generic_params(it)); + Arc::new(GenericParams::new(db, def.into(), parent_generics)) + } + + fn new( db: &impl DefDatabase2, def: GenericDefId, parent_params: Option>, @@ -161,3 +168,19 @@ impl GenericParams { vec } } + +fn parent_generic_def(db: &impl DefDatabase2, def: GenericDefId) -> Option { + let container = match def { + GenericDefId::FunctionId(it) => it.lookup(db).container, + GenericDefId::TypeAliasId(it) => it.lookup(db).container, + GenericDefId::ConstId(it) => it.lookup(db).container, + GenericDefId::EnumVariantId(it) => return Some(it.parent.into()), + GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) => return None, + }; + + match container { + ContainerId::ImplId(it) => Some(it.into()), + ContainerId::TraitId(it) => Some(it.into()), + ContainerId::ModuleId(_) => None, + } +} -- cgit v1.2.3 From 12ec946216a3637685f30ae359bc955313595a22 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 Nov 2019 20:50:34 +0300 Subject: Simplify generic params --- crates/ra_hir_def/src/generics.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'crates/ra_hir_def/src/generics.rs') diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs index f794eefbc..9e2e4c3cc 100644 --- a/crates/ra_hir_def/src/generics.rs +++ b/crates/ra_hir_def/src/generics.rs @@ -25,7 +25,6 @@ pub struct GenericParam { /// Data about the generic parameters of a function, struct, impl, etc. #[derive(Clone, PartialEq, Eq, Debug)] pub struct GenericParams { - pub def: GenericDefId, pub parent_params: Option>, pub params: Vec, pub where_predicates: Vec, @@ -56,7 +55,7 @@ impl GenericParams { parent_params: Option>, ) -> GenericParams { let mut generics = - GenericParams { def, params: Vec::new(), parent_params, where_predicates: Vec::new() }; + GenericParams { params: Vec::new(), parent_params, where_predicates: Vec::new() }; let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; // FIXME: add `: Sized` bound for everything except for `Self` in traits match def { -- cgit v1.2.3