2484: DynMap r=matklad a=matklad

Implement a `DynMap` a semi-dynamic, semi-static map, which helps to thread heterogeneously typed info in a uniform way. Totally inspired by df3bee3038/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java. 

@flodiebold wdyt? Seems like a potentially useful pattern for various source-map-like things.

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-12-06 20:28:28 +00:00 committed by GitHub
commit f18b7e18c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 362 additions and 318 deletions

7
Cargo.lock generated
View File

@ -13,6 +13,11 @@ name = "anyhow"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "anymap"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "arrayvec"
version = "0.5.1"
@ -958,6 +963,7 @@ dependencies = [
name = "ra_hir_def"
version = "0.1.0"
dependencies = [
"anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1776,6 +1782,7 @@ dependencies = [
[metadata]
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14"
"checksum anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"

View File

@ -1,9 +1,7 @@
//! FIXME: write short doc here
use either::Either;
use hir_def::{
child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId, ImplId,
LocationCtx, ModuleId, TraitId, VariantId,
child_by_source::ChildBySource, dyn_map::DynMap, keys, nameres::ModuleSource, AstItemDef,
EnumVariantId, LocationCtx, ModuleId, VariantId,
};
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
use ra_syntax::{
@ -53,8 +51,9 @@ fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) ->
impl FromSource for Function {
type Ast = ast::FnDef;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
Container::find(db, src.as_ref().map(|it| it.syntax()))?
.child_from_source(db, src)
Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::FUNCTION]
.get(&src)
.copied()
.map(Function::from)
}
}
@ -62,26 +61,29 @@ fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) ->
impl FromSource for Const {
type Ast = ast::ConstDef;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
Container::find(db, src.as_ref().map(|it| it.syntax()))?
.child_from_source(db, src)
Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::CONST]
.get(&src)
.copied()
.map(Const::from)
}
}
impl FromSource for Static {
type Ast = ast::StaticDef;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
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,
}
Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::STATIC]
.get(&src)
.copied()
.map(Static::from)
}
}
impl FromSource for TypeAlias {
type Ast = ast::TypeAliasDef;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
Container::find(db, src.as_ref().map(|it| it.syntax()))?
.child_from_source(db, src)
Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)
[keys::TYPE_ALIAS]
.get(&src)
.copied()
.map(TypeAlias::from)
}
}
@ -116,32 +118,41 @@ fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) ->
let parent_enum = src.value.parent_enum();
let src_enum = InFile { file_id: src.file_id, value: parent_enum };
let parent_enum = Enum::from_source(db, src_enum)?;
parent_enum.id.child_from_source(db, src).map(EnumVariant::from)
parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT]
.get(&src)
.copied()
.map(EnumVariant::from)
}
}
impl FromSource for StructField {
type Ast = FieldSource;
fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
let src = src.as_ref();
// FIXME this is buggy
let variant_id: VariantId = match src.value {
FieldSource::Named(ref field) => {
FieldSource::Named(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)?;
def.id.into()
}
FieldSource::Pos(ref field) => {
FieldSource::Pos(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)?;
EnumVariantId::from(def).into()
}
};
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)
let dyn_map = variant_id.child_by_source(db);
match src.value {
FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
}
.copied()
.map(StructField::from)
}
}
@ -255,21 +266,12 @@ fn find(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<Container> {
}
}
impl<CHILD, SOURCE> ChildFromSource<CHILD, SOURCE> for Container
where
TraitId: ChildFromSource<CHILD, SOURCE>,
ImplId: ChildFromSource<CHILD, SOURCE>,
ModuleId: ChildFromSource<CHILD, SOURCE>,
{
fn child_from_source(
&self,
db: &impl DefDatabase,
child_source: InFile<SOURCE>,
) -> Option<CHILD> {
impl ChildBySource for Container {
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
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),
Container::Trait(it) => it.id.child_by_source(db),
Container::ImplBlock(it) => it.id.child_by_source(db),
Container::Module(it) => it.id.child_by_source(db),
}
}
}

View File

@ -12,6 +12,7 @@ log = "0.4.5"
once_cell = "1.0.1"
rustc-hash = "1.0"
either = "1.5"
anymap = "0.12"
ra_arena = { path = "../ra_arena" }
ra_db = { path = "../ra_db" }

View File

@ -0,0 +1,139 @@
//! 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 crate::{
db::DefDatabase,
dyn_map::DynMap,
keys,
src::{HasChildSource, HasSource},
AssocItemId, EnumId, EnumVariantId, ImplId, Lookup, ModuleDefId, ModuleId, StructFieldId,
TraitId, VariantId,
};
pub trait ChildBySource {
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap;
}
impl ChildBySource for TraitId {
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
let mut res = DynMap::default();
let data = db.trait_data(*self);
for (_name, item) in data.items.iter() {
match *item {
AssocItemId::FunctionId(func) => {
let src = func.lookup(db).source(db);
res[keys::FUNCTION].insert(src, func)
}
AssocItemId::ConstId(konst) => {
let src = konst.lookup(db).source(db);
res[keys::CONST].insert(src, konst)
}
AssocItemId::TypeAliasId(ty) => {
let src = ty.lookup(db).source(db);
res[keys::TYPE_ALIAS].insert(src, ty)
}
}
}
res
}
}
impl ChildBySource for ImplId {
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
let mut res = DynMap::default();
let data = db.impl_data(*self);
for &item in data.items.iter() {
match item {
AssocItemId::FunctionId(func) => {
let src = func.lookup(db).source(db);
res[keys::FUNCTION].insert(src, func)
}
AssocItemId::ConstId(konst) => {
let src = konst.lookup(db).source(db);
res[keys::CONST].insert(src, konst)
}
AssocItemId::TypeAliasId(ty) => {
let src = ty.lookup(db).source(db);
res[keys::TYPE_ALIAS].insert(src, ty)
}
}
}
res
}
}
impl ChildBySource for ModuleId {
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
let mut res = DynMap::default();
let crate_def_map = db.crate_def_map(self.krate);
for item in crate_def_map[self.local_id].scope.declarations() {
match item {
ModuleDefId::FunctionId(func) => {
let src = func.lookup(db).source(db);
res[keys::FUNCTION].insert(src, func)
}
ModuleDefId::ConstId(konst) => {
let src = konst.lookup(db).source(db);
res[keys::CONST].insert(src, konst)
}
ModuleDefId::StaticId(statik) => {
let src = statik.lookup(db).source(db);
res[keys::STATIC].insert(src, statik)
}
ModuleDefId::TypeAliasId(ty) => {
let src = ty.lookup(db).source(db);
res[keys::TYPE_ALIAS].insert(src, ty)
}
_ => (),
}
}
res
}
}
impl ChildBySource for VariantId {
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
let mut res = DynMap::default();
let arena_map = self.child_source(db);
let arena_map = arena_map.as_ref();
for (local_id, source) in arena_map.value.iter() {
let id = StructFieldId { parent: *self, local_id };
match source {
Either::Left(source) => {
res[keys::TUPLE_FIELD].insert(arena_map.with_value(source.clone()), id)
}
Either::Right(source) => {
res[keys::RECORD_FIELD].insert(arena_map.with_value(source.clone()), id)
}
}
}
res
}
}
impl ChildBySource for EnumId {
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
let mut res = DynMap::default();
let arena_map = self.child_source(db);
let arena_map = arena_map.as_ref();
for (local_id, source) in arena_map.value.iter() {
let id = EnumVariantId { parent: *self, local_id };
res[keys::ENUM_VARIANT].insert(arena_map.with_value(source.clone()), id)
}
res
}
}

View File

@ -1,276 +0,0 @@
//! 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

@ -0,0 +1,108 @@
//! This module defines a `DynMap` -- a container for heterogeneous maps.
//!
//! This means that `DynMap` stores a bunch of hash maps inside, and those maps
//! can be of different types.
//!
//! It is used like this:
//!
//! ```
//! // keys define submaps of a `DynMap`
//! const STRING_TO_U32: Key<String, u32> = Key::new();
//! const U32_TO_VEC: Key<u32, Vec<bool>> = Key::new();
//!
//! // Note: concrete type, no type params!
//! let mut map = DynMap::new();
//!
//! // To access a specific map, index the `DynMap` by `Key`:
//! map[STRING_TO_U32].insert("hello".to_string(), 92);
//! let value = map[U32_TO_VEC].get(92);
//! assert!(value.is_none());
//! ```
//!
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
//! a coincidence.
use std::{
hash::Hash,
marker::PhantomData,
ops::{Index, IndexMut},
};
use anymap::Map;
use rustc_hash::FxHashMap;
pub struct Key<K, V, P = (K, V)> {
_phantom: PhantomData<(K, V, P)>,
}
impl<K, V, P> Key<K, V, P> {
pub(crate) const fn new() -> Key<K, V, P> {
Key { _phantom: PhantomData }
}
}
impl<K, V, P> Copy for Key<K, V, P> {}
impl<K, V, P> Clone for Key<K, V, P> {
fn clone(&self) -> Key<K, V, P> {
*self
}
}
pub trait Policy {
type K;
type V;
fn insert(map: &mut DynMap, key: Self::K, value: Self::V);
fn get<'a>(map: &'a DynMap, key: &Self::K) -> Option<&'a Self::V>;
}
impl<K: Hash + Eq + 'static, V: 'static> Policy for (K, V) {
type K = K;
type V = V;
fn insert(map: &mut DynMap, key: K, value: V) {
map.map.entry::<FxHashMap<K, V>>().or_insert_with(Default::default).insert(key, value);
}
fn get<'a>(map: &'a DynMap, key: &K) -> Option<&'a V> {
map.map.get::<FxHashMap<K, V>>()?.get(key)
}
}
pub struct DynMap {
pub(crate) map: Map,
}
impl Default for DynMap {
fn default() -> Self {
DynMap { map: Map::new() }
}
}
#[repr(transparent)]
pub struct KeyMap<KEY> {
map: DynMap,
_phantom: PhantomData<KEY>,
}
impl<P: Policy> KeyMap<Key<P::K, P::V, P>> {
pub fn insert(&mut self, key: P::K, value: P::V) {
P::insert(&mut self.map, key, value)
}
pub fn get(&self, key: &P::K) -> Option<&P::V> {
P::get(&self.map, key)
}
}
impl<P: Policy> Index<Key<P::K, P::V, P>> for DynMap {
type Output = KeyMap<Key<P::K, P::V, P>>;
fn index(&self, _key: Key<P::K, P::V, P>) -> &Self::Output {
// Safe due to `#[repr(transparent)]`.
unsafe { std::mem::transmute::<&DynMap, &KeyMap<Key<P::K, P::V, P>>>(self) }
}
}
impl<P: Policy> IndexMut<Key<P::K, P::V, P>> for DynMap {
fn index_mut(&mut self, _key: Key<P::K, P::V, P>) -> &mut Self::Output {
// Safe due to `#[repr(transparent)]`.
unsafe { std::mem::transmute::<&mut DynMap, &mut KeyMap<Key<P::K, P::V, P>>>(self) }
}
}

View File

@ -0,0 +1,48 @@
//! keys to be used with `DynMap`
use std::marker::PhantomData;
use hir_expand::InFile;
use ra_syntax::{ast, AstNode, AstPtr};
use rustc_hash::FxHashMap;
use crate::{
dyn_map::{DynMap, Policy},
ConstId, EnumVariantId, FunctionId, StaticId, StructFieldId, TypeAliasId,
};
type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
pub const FUNCTION: Key<ast::FnDef, FunctionId> = Key::new();
pub const CONST: Key<ast::ConstDef, ConstId> = Key::new();
pub const STATIC: Key<ast::StaticDef, StaticId> = Key::new();
pub const ENUM_VARIANT: Key<ast::EnumVariant, EnumVariantId> = Key::new();
pub const TYPE_ALIAS: Key<ast::TypeAliasDef, TypeAliasId> = Key::new();
pub const TUPLE_FIELD: Key<ast::TupleFieldDef, StructFieldId> = Key::new();
pub const RECORD_FIELD: Key<ast::RecordFieldDef, StructFieldId> = Key::new();
/// 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.
pub struct AstPtrPolicy<AST, ID> {
_phantom: PhantomData<(AST, ID)>,
}
impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> {
type K = InFile<AST>;
type V = ID;
fn insert(map: &mut DynMap, key: InFile<AST>, value: ID) {
let key = key.as_ref().map(AstPtr::new);
map.map
.entry::<FxHashMap<InFile<AstPtr<AST>>, ID>>()
.or_insert_with(Default::default)
.insert(key, value);
}
fn get<'a>(map: &'a DynMap, key: &InFile<AST>) -> Option<&'a ID> {
let key = key.as_ref().map(AstPtr::new);
map.map.get::<FxHashMap<InFile<AstPtr<AST>>, ID>>()?.get(&key)
}
}

View File

@ -16,6 +16,9 @@
pub mod diagnostics;
pub mod per_ns;
pub mod dyn_map;
pub mod keys;
pub mod adt;
pub mod data;
pub mod generics;
@ -30,7 +33,7 @@
pub mod nameres;
pub mod src;
pub mod child_from_source;
pub mod child_by_source;
#[cfg(test)]
mod test_db;

View File

@ -11,8 +11,8 @@
use std::sync::Arc;
use hir_def::{
body::BodySourceMap, child_from_source::ChildFromSource, db::DefDatabase, nameres::CrateDefMap,
AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, keys,
nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
};
use hir_expand::InFile;
use insta::assert_snapshot;
@ -33,7 +33,9 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
let module = db.module_for_file(pos.file_id);
let func = module.child_from_source(db, InFile::new(pos.file_id.into(), fn_def)).unwrap();
let func = *module.child_by_source(db)[keys::FUNCTION]
.get(&InFile::new(pos.file_id.into(), fn_def))
.unwrap();
let (_body, source_map) = db.body_with_source_map(func.into());
if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) {

View File

@ -1,6 +1,10 @@
//! FIXME: write short doc here
use std::{iter::successors, marker::PhantomData};
use std::{
hash::{Hash, Hasher},
iter::successors,
marker::PhantomData,
};
use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange};
@ -43,7 +47,7 @@ pub fn cast<N: AstNode>(self) -> Option<AstPtr<N>> {
}
/// Like `SyntaxNodePtr`, but remembers the type of node
#[derive(Debug, Hash)]
#[derive(Debug)]
pub struct AstPtr<N: AstNode> {
raw: SyntaxNodePtr,
_ty: PhantomData<fn() -> N>,
@ -64,6 +68,12 @@ fn eq(&self, other: &AstPtr<N>) -> bool {
}
}
impl<N: AstNode> Hash for AstPtr<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.raw.hash(state)
}
}
impl<N: AstNode> AstPtr<N> {
pub fn new(node: &N) -> AstPtr<N> {
AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData }