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_def/src/child_from_source.rs | 276 +++++++++++++++++++++++++++++ crates/ra_hir_def/src/lib.rs | 1 + 2 files changed, 277 insertions(+) create mode 100644 crates/ra_hir_def/src/child_from_source.rs (limited to 'crates/ra_hir_def') 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