2480: New from sorce infra r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-12-05 15:59:22 +00:00 committed by GitHub
commit 217a6fa4a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 325 additions and 99 deletions

View file

@ -1,17 +1,20 @@
//! FIXME: write short doc here //! 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, ImplId,
LocationCtx, ModuleId, TraitId, VariantId,
};
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode, NameOwner}, ast::{self, AstNode, NameOwner},
match_ast, AstPtr, SyntaxNode, match_ast, SyntaxNode,
}; };
use crate::{ use crate::{
db::{AstDatabase, DefDatabase, HirDatabase}, db::{AstDatabase, DefDatabase, HirDatabase},
AssocItem, Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
InFile, Local, MacroDef, Module, ModuleDef, Static, Struct, StructField, Trait, TypeAlias, MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, Union,
Union, VariantDef,
}; };
pub trait FromSource: Sized { pub trait FromSource: Sized {
@ -50,98 +53,36 @@ impl FromSource for Trait {
impl FromSource for Function { impl FromSource for Function {
type Ast = ast::FnDef; type Ast = ast::FnDef;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { Container::find(db, src.as_ref().map(|it| it.syntax()))?
Container::Trait(it) => it.items(db), .child_from_source(db, src)
Container::ImplBlock(it) => it.items(db), .map(Function::from)
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))
} }
} }
impl FromSource for Const { impl FromSource for Const {
type Ast = ast::ConstDef; type Ast = ast::ConstDef;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { Container::find(db, src.as_ref().map(|it| it.syntax()))?
Container::Trait(it) => it.items(db), .child_from_source(db, src)
Container::ImplBlock(it) => it.items(db), .map(Const::from)
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))
} }
} }
impl FromSource for Static { impl FromSource for Static {
type Ast = ast::StaticDef; type Ast = ast::StaticDef;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
let module = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
Container::Module(it) => it, Container::Module(it) => it.id.child_from_source(db, src).map(Static::from),
Container::Trait(_) | Container::ImplBlock(_) => return None, Container::Trait(_) | Container::ImplBlock(_) => 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))
} }
} }
impl FromSource for TypeAlias { impl FromSource for TypeAlias {
type Ast = ast::TypeAliasDef; type Ast = ast::TypeAliasDef;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { Container::find(db, src.as_ref().map(|it| it.syntax()))?
Container::Trait(it) => it.items(db), .child_from_source(db, src)
Container::ImplBlock(it) => it.items(db), .map(TypeAlias::from)
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))
} }
} }
@ -174,34 +115,33 @@ impl FromSource for EnumVariant {
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
let parent_enum = src.value.parent_enum(); let parent_enum = src.value.parent_enum();
let src_enum = InFile { file_id: src.file_id, 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); let parent_enum = Enum::from_source(db, src_enum)?;
variants.into_iter().find(|v| same_source(&v.source(db), &src)) parent_enum.id.child_from_source(db, src).map(EnumVariant::from)
} }
} }
impl FromSource for StructField { impl FromSource for StructField {
type Ast = FieldSource; type Ast = FieldSource;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
let variant_def: VariantDef = match src.value { let variant_id: VariantId = match src.value {
FieldSource::Named(ref field) => { FieldSource::Named(ref field) => {
let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?; let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
let src = InFile { file_id: src.file_id, value }; let src = InFile { file_id: src.file_id, value };
let def = Struct::from_source(db, src)?; let def = Struct::from_source(db, src)?;
VariantDef::from(def) def.id.into()
} }
FieldSource::Pos(ref field) => { FieldSource::Pos(ref field) => {
let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?; let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
let src = InFile { file_id: src.file_id, value }; let src = InFile { file_id: src.file_id, value };
let def = EnumVariant::from_source(db, src)?; let def = EnumVariant::from_source(db, src)?;
VariantDef::from(def) EnumVariantId::from(def).into()
} }
}; };
variant_def let src = src.map(|field_source| match field_source {
.variant_data(db) FieldSource::Pos(it) => Either::Left(it),
.fields() FieldSource::Named(it) => Either::Right(it),
.iter() });
.map(|(id, _)| StructField { parent: variant_def, id }) variant_id.child_from_source(db, src).map(StructField::from)
.find(|f| f.source(db) == src)
} }
} }
@ -315,12 +255,21 @@ impl Container {
} }
} }
/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are impl<CHILD, SOURCE> ChildFromSource<CHILD, SOURCE> for Container
/// equal if they point to exactly the same object. where
/// TraitId: ChildFromSource<CHILD, SOURCE>,
/// In general, we do not guarantee that we have exactly one instance of a ImplId: ChildFromSource<CHILD, SOURCE>,
/// syntax tree for each file. We probably should add such guarantee, but, for ModuleId: ChildFromSource<CHILD, SOURCE>,
/// the time being, we will use identity-less AstPtr comparison. {
fn same_source<N: AstNode>(s1: &InFile<N>, s2: &InFile<N>) -> bool { fn child_from_source(
s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new) &self,
db: &impl DefDatabase,
child_source: InFile<SOURCE>,
) -> Option<CHILD> {
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),
}
}
} }

View file

@ -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<CHILD, SOURCE> {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<SOURCE>,
) -> Option<CHILD>;
}
impl ChildFromSource<FunctionId, ast::FnDef> for TraitId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::FnDef>,
) -> Option<FunctionId> {
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<FunctionId, ast::FnDef> for ImplId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::FnDef>,
) -> Option<FunctionId> {
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<FunctionId, ast::FnDef> for ModuleId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::FnDef>,
) -> Option<FunctionId> {
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<ConstId, ast::ConstDef> for TraitId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::ConstDef>,
) -> Option<ConstId> {
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<ConstId, ast::ConstDef> for ImplId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::ConstDef>,
) -> Option<ConstId> {
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<ConstId, ast::ConstDef> for ModuleId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::ConstDef>,
) -> Option<ConstId> {
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<TypeAliasId, ast::TypeAliasDef> for TraitId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::TypeAliasDef>,
) -> Option<TypeAliasId> {
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<TypeAliasId, ast::TypeAliasDef> for ImplId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::TypeAliasDef>,
) -> Option<TypeAliasId> {
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<TypeAliasId, ast::TypeAliasDef> for ModuleId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::TypeAliasDef>,
) -> Option<TypeAliasId> {
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<StaticId, ast::StaticDef> for ModuleId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::StaticDef>,
) -> Option<StaticId> {
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<StructFieldId, Either<ast::TupleFieldDef, ast::RecordFieldDef>> for VariantId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<Either<ast::TupleFieldDef, ast::RecordFieldDef>>,
) -> Option<StructFieldId> {
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<EnumVariantId, ast::EnumVariant> for EnumId {
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<ast::EnumVariant>,
) -> Option<EnumVariantId> {
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<N: AstNode>(s1: &InFile<N>, s2: &InFile<N>) -> bool {
s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new)
}

View file

@ -30,6 +30,7 @@ mod trace;
pub mod nameres; pub mod nameres;
pub mod src; pub mod src;
pub mod child_from_source;
#[cfg(test)] #[cfg(test)]
mod test_db; mod test_db;