From 0c0ce1ae418a2f3f4fc125bd701cdb327f607002 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 5 Dec 2019 15:16:59 +0100 Subject: Introduce ChildFromSource --- crates/ra_hir/src/from_source.rs | 141 +++++---------- crates/ra_hir_def/src/child_from_source.rs | 276 +++++++++++++++++++++++++++++ crates/ra_hir_def/src/lib.rs | 1 + 3 files changed, 317 insertions(+), 101 deletions(-) create mode 100644 crates/ra_hir_def/src/child_from_source.rs diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs index 18d87f6d7..58203c721 100644 --- a/crates/ra_hir/src/from_source.rs +++ b/crates/ra_hir/src/from_source.rs @@ -1,17 +1,20 @@ //! FIXME: write short doc here +use either::Either; -use hir_def::{nameres::ModuleSource, AstItemDef, LocationCtx, ModuleId}; +use hir_def::{ + child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId, + LocationCtx, ModuleId, VariantId, +}; use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; use ra_syntax::{ ast::{self, AstNode, NameOwner}, - match_ast, AstPtr, SyntaxNode, + match_ast, SyntaxNode, }; use crate::{ db::{AstDatabase, DefDatabase, HirDatabase}, - AssocItem, Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, - InFile, Local, MacroDef, Module, ModuleDef, Static, Struct, StructField, Trait, TypeAlias, - Union, VariantDef, + Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local, + MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, Union, }; pub trait FromSource: Sized { @@ -50,98 +53,45 @@ impl FromSource for Trait { impl FromSource for Function { type Ast = ast::FnDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { - let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { - Container::Trait(it) => it.items(db), - Container::ImplBlock(it) => it.items(db), - Container::Module(m) => { - return m - .declarations(db) - .into_iter() - .filter_map(|it| match it { - ModuleDef::Function(it) => Some(it), - _ => None, - }) - .find(|it| same_source(&it.source(db), &src)) - } - }; - items - .into_iter() - .filter_map(|it| match it { - AssocItem::Function(it) => Some(it), - _ => None, - }) - .find(|it| same_source(&it.source(db), &src)) + match Container::find(db, src.as_ref().map(|it| it.syntax()))? { + Container::Trait(it) => it.id.child_from_source(db, src), + Container::ImplBlock(it) => it.id.child_from_source(db, src), + Container::Module(it) => it.id.child_from_source(db, src), + } + .map(Function::from) } } impl FromSource for Const { type Ast = ast::ConstDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { - let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { - Container::Trait(it) => it.items(db), - Container::ImplBlock(it) => it.items(db), - Container::Module(m) => { - return m - .declarations(db) - .into_iter() - .filter_map(|it| match it { - ModuleDef::Const(it) => Some(it), - _ => None, - }) - .find(|it| same_source(&it.source(db), &src)) - } - }; - items - .into_iter() - .filter_map(|it| match it { - AssocItem::Const(it) => Some(it), - _ => None, - }) - .find(|it| same_source(&it.source(db), &src)) + match Container::find(db, src.as_ref().map(|it| it.syntax()))? { + Container::Trait(it) => it.id.child_from_source(db, src), + Container::ImplBlock(it) => it.id.child_from_source(db, src), + Container::Module(it) => it.id.child_from_source(db, src), + } + .map(Const::from) } } impl FromSource for Static { type Ast = ast::StaticDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { - let module = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { - Container::Module(it) => it, - Container::Trait(_) | Container::ImplBlock(_) => return None, - }; - module - .declarations(db) - .into_iter() - .filter_map(|it| match it { - ModuleDef::Static(it) => Some(it), - _ => None, - }) - .find(|it| same_source(&it.source(db), &src)) + match Container::find(db, src.as_ref().map(|it| it.syntax()))? { + Container::Module(it) => it.id.child_from_source(db, src).map(Static::from), + Container::Trait(_) | Container::ImplBlock(_) => None, + } } } impl FromSource for TypeAlias { type Ast = ast::TypeAliasDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { - let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { - Container::Trait(it) => it.items(db), - Container::ImplBlock(it) => it.items(db), - Container::Module(m) => { - return m - .declarations(db) - .into_iter() - .filter_map(|it| match it { - ModuleDef::TypeAlias(it) => Some(it), - _ => None, - }) - .find(|it| same_source(&it.source(db), &src)) - } - }; - items - .into_iter() - .filter_map(|it| match it { - AssocItem::TypeAlias(it) => Some(it), - _ => None, - }) - .find(|it| same_source(&it.source(db), &src)) + match Container::find(db, src.as_ref().map(|it| it.syntax()))? { + Container::Trait(it) => it.id.child_from_source(db, src), + Container::ImplBlock(it) => it.id.child_from_source(db, src), + Container::Module(it) => it.id.child_from_source(db, src), + } + .map(TypeAlias::from) } } @@ -174,34 +124,33 @@ impl FromSource for EnumVariant { fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { let parent_enum = src.value.parent_enum(); let src_enum = InFile { file_id: src.file_id, value: parent_enum }; - let variants = Enum::from_source(db, src_enum)?.variants(db); - variants.into_iter().find(|v| same_source(&v.source(db), &src)) + let parent_enum = Enum::from_source(db, src_enum)?; + parent_enum.id.child_from_source(db, src).map(EnumVariant::from) } } impl FromSource for StructField { type Ast = FieldSource; fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { - let variant_def: VariantDef = match src.value { + let variant_id: VariantId = match src.value { FieldSource::Named(ref field) => { let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?; let src = InFile { file_id: src.file_id, value }; let def = Struct::from_source(db, src)?; - VariantDef::from(def) + def.id.into() } FieldSource::Pos(ref field) => { let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?; let src = InFile { file_id: src.file_id, value }; let def = EnumVariant::from_source(db, src)?; - VariantDef::from(def) + EnumVariantId::from(def).into() } }; - variant_def - .variant_data(db) - .fields() - .iter() - .map(|(id, _)| StructField { parent: variant_def, id }) - .find(|f| f.source(db) == src) + let src = src.map(|field_source| match field_source { + FieldSource::Pos(it) => Either::Left(it), + FieldSource::Named(it) => Either::Right(it), + }); + variant_id.child_from_source(db, src).map(StructField::from) } } @@ -314,13 +263,3 @@ impl Container { Some(Container::Module(c)) } } - -/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are -/// equal if they point to exactly the same object. -/// -/// In general, we do not guarantee that we have exactly one instance of a -/// syntax tree for each file. We probably should add such guarantee, but, for -/// the time being, we will use identity-less AstPtr comparison. -fn same_source(s1: &InFile, s2: &InFile) -> bool { - s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new) -} diff --git a/crates/ra_hir_def/src/child_from_source.rs b/crates/ra_hir_def/src/child_from_source.rs new file mode 100644 index 000000000..37d4b7870 --- /dev/null +++ b/crates/ra_hir_def/src/child_from_source.rs @@ -0,0 +1,276 @@ +//! When *constructing* `hir`, we start at some parent syntax node and recursively +//! lower the children. +//! +//! This modules allows one to go in the opposite direction: start with a syntax +//! node for a *child*, and get its hir. + +use either::Either; +use hir_expand::InFile; +use ra_syntax::{ast, AstNode, AstPtr}; + +use crate::{ + db::DefDatabase, + src::{HasChildSource, HasSource}, + AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, ImplId, Lookup, ModuleDefId, ModuleId, + StaticId, StructFieldId, TraitId, TypeAliasId, VariantId, +}; + +pub trait ChildFromSource { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option; +} + +impl ChildFromSource for TraitId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let data = db.trait_data(*self); + data.items + .iter() + .filter_map(|(_, item)| match item { + AssocItemId::FunctionId(it) => Some(*it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }) + } +} + +impl ChildFromSource for ImplId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let data = db.impl_data(*self); + data.items + .iter() + .filter_map(|item| match item { + AssocItemId::FunctionId(it) => Some(*it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }) + } +} + +impl ChildFromSource for ModuleId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let crate_def_map = db.crate_def_map(self.krate); + let res = crate_def_map[self.local_id] + .scope + .declarations() + .filter_map(|item| match item { + ModuleDefId::FunctionId(it) => Some(it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }); + res + } +} + +impl ChildFromSource for TraitId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let data = db.trait_data(*self); + data.items + .iter() + .filter_map(|(_, item)| match item { + AssocItemId::ConstId(it) => Some(*it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }) + } +} + +impl ChildFromSource for ImplId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let data = db.impl_data(*self); + data.items + .iter() + .filter_map(|item| match item { + AssocItemId::ConstId(it) => Some(*it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }) + } +} + +impl ChildFromSource for ModuleId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let crate_def_map = db.crate_def_map(self.krate); + let res = crate_def_map[self.local_id] + .scope + .declarations() + .filter_map(|item| match item { + ModuleDefId::ConstId(it) => Some(it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }); + res + } +} + +impl ChildFromSource for TraitId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let data = db.trait_data(*self); + data.items + .iter() + .filter_map(|(_, item)| match item { + AssocItemId::TypeAliasId(it) => Some(*it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }) + } +} + +impl ChildFromSource for ImplId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let data = db.impl_data(*self); + data.items + .iter() + .filter_map(|item| match item { + AssocItemId::TypeAliasId(it) => Some(*it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }) + } +} + +impl ChildFromSource for ModuleId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let crate_def_map = db.crate_def_map(self.krate); + let res = crate_def_map[self.local_id] + .scope + .declarations() + .filter_map(|item| match item { + ModuleDefId::TypeAliasId(it) => Some(it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }); + res + } +} + +impl ChildFromSource for ModuleId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let crate_def_map = db.crate_def_map(self.krate); + let res = crate_def_map[self.local_id] + .scope + .declarations() + .filter_map(|item| match item { + ModuleDefId::StaticId(it) => Some(it), + _ => None, + }) + .find(|func| { + let source = func.lookup(db).source(db); + same_source(&source, &child_source) + }); + res + } +} + +impl ChildFromSource> for VariantId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile>, + ) -> Option { + let arena_map = self.child_source(db); + let (local_id, _) = arena_map.as_ref().value.iter().find(|(_local_id, source)| { + child_source.file_id == arena_map.file_id + && match (source, &child_source.value) { + (Either::Left(a), Either::Left(b)) => AstPtr::new(a) == AstPtr::new(b), + (Either::Right(a), Either::Right(b)) => AstPtr::new(a) == AstPtr::new(b), + _ => false, + } + })?; + Some(StructFieldId { parent: *self, local_id }) + } +} + +impl ChildFromSource for EnumId { + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + let arena_map = self.child_source(db); + let (local_id, _) = arena_map.as_ref().value.iter().find(|(_local_id, source)| { + child_source.file_id == arena_map.file_id + && AstPtr::new(*source) == AstPtr::new(&child_source.value) + })?; + Some(EnumVariantId { parent: *self, local_id }) + } +} + +/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are +/// equal if they point to exactly the same object. +/// +/// In general, we do not guarantee that we have exactly one instance of a +/// syntax tree for each file. We probably should add such guarantee, but, for +/// the time being, we will use identity-less AstPtr comparison. +fn same_source(s1: &InFile, s2: &InFile) -> bool { + s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new) +} diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index cfeacfded..e02622f62 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -30,6 +30,7 @@ mod trace; pub mod nameres; pub mod src; +pub mod child_from_source; #[cfg(test)] mod test_db; -- cgit v1.2.3 From 1a567f5ca28b40c1cd744c9123a59695fab351de Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 5 Dec 2019 16:53:17 +0100 Subject: Reduce copy-paste --- crates/ra_hir/src/from_source.rs | 50 ++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs index 58203c721..6fa947759 100644 --- a/crates/ra_hir/src/from_source.rs +++ b/crates/ra_hir/src/from_source.rs @@ -2,8 +2,8 @@ use either::Either; use hir_def::{ - child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId, - LocationCtx, ModuleId, VariantId, + child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId, ImplId, + LocationCtx, ModuleId, TraitId, VariantId, }; use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; use ra_syntax::{ @@ -53,24 +53,18 @@ impl FromSource for Trait { impl FromSource for Function { type Ast = ast::FnDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { - match Container::find(db, src.as_ref().map(|it| it.syntax()))? { - Container::Trait(it) => it.id.child_from_source(db, src), - Container::ImplBlock(it) => it.id.child_from_source(db, src), - Container::Module(it) => it.id.child_from_source(db, src), - } - .map(Function::from) + Container::find(db, src.as_ref().map(|it| it.syntax()))? + .child_from_source(db, src) + .map(Function::from) } } impl FromSource for Const { type Ast = ast::ConstDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { - match Container::find(db, src.as_ref().map(|it| it.syntax()))? { - Container::Trait(it) => it.id.child_from_source(db, src), - Container::ImplBlock(it) => it.id.child_from_source(db, src), - Container::Module(it) => it.id.child_from_source(db, src), - } - .map(Const::from) + Container::find(db, src.as_ref().map(|it| it.syntax()))? + .child_from_source(db, src) + .map(Const::from) } } impl FromSource for Static { @@ -86,12 +80,9 @@ impl FromSource for Static { impl FromSource for TypeAlias { type Ast = ast::TypeAliasDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile) -> Option { - match Container::find(db, src.as_ref().map(|it| it.syntax()))? { - Container::Trait(it) => it.id.child_from_source(db, src), - Container::ImplBlock(it) => it.id.child_from_source(db, src), - Container::Module(it) => it.id.child_from_source(db, src), - } - .map(TypeAlias::from) + Container::find(db, src.as_ref().map(|it| it.syntax()))? + .child_from_source(db, src) + .map(TypeAlias::from) } } @@ -263,3 +254,22 @@ impl Container { Some(Container::Module(c)) } } + +impl ChildFromSource for Container +where + TraitId: ChildFromSource, + ImplId: ChildFromSource, + ModuleId: ChildFromSource, +{ + fn child_from_source( + &self, + db: &impl DefDatabase, + child_source: InFile, + ) -> Option { + match self { + Container::Trait(it) => it.id.child_from_source(db, child_source), + Container::ImplBlock(it) => it.id.child_from_source(db, child_source), + Container::Module(it) => it.id.child_from_source(db, child_source), + } + } +} -- cgit v1.2.3