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/src/code_model.rs | 25 ++++++++------ crates/ra_hir/src/code_model/src.rs | 3 +- crates/ra_hir/src/from_source.rs | 65 +++++++++++++++++++++++++++++++++---- crates/ra_hir/src/source_binder.rs | 2 +- 4 files changed, 76 insertions(+), 19 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index f436d5d5e..c49190a0f 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -12,7 +12,8 @@ use hir_def::{ builtin_type::BuiltinType, traits::TraitData, type_ref::{Mutability, TypeRef}, - AssocItemId, CrateModuleId, ImplId, LocalEnumVariantId, LocalStructFieldId, ModuleId, UnionId, + AssocItemId, CrateModuleId, FunctionContainerId, HasModule, ImplId, LocalEnumVariantId, + LocalStructFieldId, Lookup, ModuleId, UnionId, }; use hir_expand::{ diagnostics::DiagnosticSink, @@ -647,7 +648,7 @@ impl FnData { impl Function { pub fn module(self, db: &impl DefDatabase) -> Module { - Module { id: self.id.module(db) } + self.id.lookup(db).module(db).into() } pub fn krate(self, db: &impl DefDatabase) -> Option { @@ -680,21 +681,25 @@ impl Function { /// The containing impl block, if this is a method. pub fn impl_block(self, db: &impl DefDatabase) -> Option { - ImplBlock::containing(db, self.into()) + match self.container(db) { + Some(Container::ImplBlock(it)) => Some(it), + _ => None, + } } /// The containing trait, if this is a trait method definition. pub fn parent_trait(self, db: &impl DefDatabase) -> Option { - db.trait_items_index(self.module(db).id).get_parent_trait(self.id.into()).map(Trait::from) + match self.container(db) { + Some(Container::Trait(it)) => Some(it), + _ => None, + } } pub fn container(self, db: &impl DefDatabase) -> Option { - if let Some(impl_block) = self.impl_block(db) { - Some(impl_block.into()) - } else if let Some(trait_) = self.parent_trait(db) { - Some(trait_.into()) - } else { - None + match self.id.lookup(db).container { + FunctionContainerId::TraitId(it) => Some(Container::Trait(it.into())), + FunctionContainerId::ImplId(it) => Some(Container::ImplBlock(it.into())), + FunctionContainerId::ModuleId(_) => None, } } diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs index 556417b0f..91cab7414 100644 --- a/crates/ra_hir/src/code_model/src.rs +++ b/crates/ra_hir/src/code_model/src.rs @@ -1,5 +1,6 @@ //! FIXME: write short doc here +use hir_def::{HasSource as _, Lookup}; use ra_syntax::ast::{self, AstNode}; use crate::{ @@ -113,7 +114,7 @@ impl HasSource for EnumVariant { impl HasSource for Function { type Ast = ast::FnDef; fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source { - self.id.source(db) + self.id.lookup(db).source(db) } } impl HasSource for Const { diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs index f4dca25cb..303d5f138 100644 --- a/crates/ra_hir/src/from_source.rs +++ b/crates/ra_hir/src/from_source.rs @@ -4,15 +4,15 @@ use hir_def::{ModuleId, StructId, StructOrUnionId, UnionId}; use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; use ra_syntax::{ ast::{self, AstNode, NameOwner}, - match_ast, + match_ast, AstPtr, }; use crate::{ db::{AstDatabase, DefDatabase, HirDatabase}, ids::{AstItemDef, LocationCtx}, - Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasBody, HasSource, ImplBlock, - Local, MacroDef, Module, ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, - Union, VariantDef, + AssocItem, Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasBody, HasSource, + ImplBlock, Local, MacroDef, Module, ModuleDef, ModuleSource, Source, Static, Struct, + StructField, Trait, TypeAlias, Union, VariantDef, }; pub trait FromSource: Sized { @@ -52,10 +52,51 @@ impl FromSource for Trait { impl FromSource for Function { type Ast = ast::FnDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source) -> Option { - let id = from_source(db, src)?; - Some(Function { id }) + // FIXME: this doesn't try to handle nested declarations + for container in src.value.syntax().ancestors() { + let res = match_ast! { + match container { + ast::TraitDef(it) => { + let c = Trait::from_source(db, src.with_value(it))?; + c.items(db) + .into_iter() + .filter_map(|it| match it { + AssocItem::Function(it) => Some(it), + _ => None + }) + .find(|it| same_source(&it.source(db), &src))? + }, + ast::ImplBlock(it) => { + let c = ImplBlock::from_source(db, src.with_value(it))?; + c.items(db) + .into_iter() + .filter_map(|it| match it { + AssocItem::Function(it) => Some(it), + _ => None + }) + .find(|it| same_source(&it.source(db), &src))? + + }, + _ => { continue }, + } + }; + return Some(res); + } + + let module_source = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax())); + let c = Module::from_definition(db, src.with_value(module_source))?; + let res = c + .declarations(db) + .into_iter() + .filter_map(|it| match it { + ModuleDef::Function(it) => Some(it), + _ => None, + }) + .find(|it| same_source(&it.source(db), &src)); + res } } + impl FromSource for Const { type Ast = ast::ConstDef; fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source) -> Option { @@ -108,7 +149,7 @@ impl FromSource for EnumVariant { let parent_enum = src.value.parent_enum(); let src_enum = Source { file_id: src.file_id, value: parent_enum }; let variants = Enum::from_source(db, src_enum)?.variants(db); - variants.into_iter().find(|v| v.source(db) == src) + variants.into_iter().find(|v| same_source(&v.source(db), &src)) } } @@ -216,3 +257,13 @@ where let ctx = LocationCtx::new(db, module.id, src.file_id); Some(DEF::from_ast(ctx, &src.value)) } + +/// 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 guanratree, but, for +/// the time being, we will use identity-less AstPtr comparison. +fn same_source(s1: &Source, s2: &Source) -> bool { + s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new) +} diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a1c1daacd..5d9e22ee2 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -70,7 +70,7 @@ fn def_with_body_from_child_node( child.value.ancestors().find_map(|node| { match_ast! { match node { - ast::FnDef(def) => { Some(Function {id: ctx.to_def(&def) }.into()) }, + ast::FnDef(def) => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); }, ast::ConstDef(def) => { Some(Const { id: ctx.to_def(&def) }.into()) }, ast::StaticDef(def) => { Some(Static { id: ctx.to_def(&def) }.into()) }, _ => { None }, -- cgit v1.2.3