mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-26 03:45:04 +00:00
DynMap
This might, or might not help us to reduce boilerplate associated with plumbing values from analysis to the IDE layer
This commit is contained in:
parent
d2b210a02e
commit
8c86963d47
10 changed files with 362 additions and 318 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -13,6 +13,11 @@ name = "anyhow"
|
||||||
version = "1.0.25"
|
version = "1.0.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -958,6 +963,7 @@ dependencies = [
|
||||||
name = "ra_hir_def"
|
name = "ra_hir_def"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
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)",
|
"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)",
|
"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)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1776,6 +1782,7 @@ dependencies = [
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
"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 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 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 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"
|
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
use either::Either;
|
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId, ImplId,
|
child_by_source::ChildBySource, dyn_map::DynMap, keys, nameres::ModuleSource, AstItemDef,
|
||||||
LocationCtx, ModuleId, TraitId, VariantId,
|
EnumVariantId, LocationCtx, ModuleId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
|
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
|
@ -53,8 +51,9 @@ 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> {
|
||||||
Container::find(db, src.as_ref().map(|it| it.syntax()))?
|
Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::FUNCTION]
|
||||||
.child_from_source(db, src)
|
.get(&src)
|
||||||
|
.copied()
|
||||||
.map(Function::from)
|
.map(Function::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,26 +61,29 @@ impl FromSource for Function {
|
||||||
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> {
|
||||||
Container::find(db, src.as_ref().map(|it| it.syntax()))?
|
Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::CONST]
|
||||||
.child_from_source(db, src)
|
.get(&src)
|
||||||
|
.copied()
|
||||||
.map(Const::from)
|
.map(Const::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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> {
|
||||||
match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
|
Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::STATIC]
|
||||||
Container::Module(it) => it.id.child_from_source(db, src).map(Static::from),
|
.get(&src)
|
||||||
Container::Trait(_) | Container::ImplBlock(_) => None,
|
.copied()
|
||||||
}
|
.map(Static::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
||||||
Container::find(db, src.as_ref().map(|it| it.syntax()))?
|
Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)
|
||||||
.child_from_source(db, src)
|
[keys::TYPE_ALIAS]
|
||||||
|
.get(&src)
|
||||||
|
.copied()
|
||||||
.map(TypeAlias::from)
|
.map(TypeAlias::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,32 +118,41 @@ impl FromSource for EnumVariant {
|
||||||
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 parent_enum = Enum::from_source(db, src_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 {
|
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 src = src.as_ref();
|
||||||
|
|
||||||
|
// FIXME this is buggy
|
||||||
let variant_id: VariantId = match src.value {
|
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 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)?;
|
||||||
def.id.into()
|
def.id.into()
|
||||||
}
|
}
|
||||||
FieldSource::Pos(ref field) => {
|
FieldSource::Pos(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)?;
|
||||||
EnumVariantId::from(def).into()
|
EnumVariantId::from(def).into()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let src = src.map(|field_source| match field_source {
|
|
||||||
FieldSource::Pos(it) => Either::Left(it),
|
let dyn_map = variant_id.child_by_source(db);
|
||||||
FieldSource::Named(it) => Either::Right(it),
|
match src.value {
|
||||||
});
|
FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
|
||||||
variant_id.child_from_source(db, src).map(StructField::from)
|
FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
|
||||||
|
}
|
||||||
|
.copied()
|
||||||
|
.map(StructField::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,21 +266,12 @@ impl Container {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CHILD, SOURCE> ChildFromSource<CHILD, SOURCE> for Container
|
impl ChildBySource for Container {
|
||||||
where
|
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||||
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> {
|
|
||||||
match self {
|
match self {
|
||||||
Container::Trait(it) => it.id.child_from_source(db, child_source),
|
Container::Trait(it) => it.id.child_by_source(db),
|
||||||
Container::ImplBlock(it) => it.id.child_from_source(db, child_source),
|
Container::ImplBlock(it) => it.id.child_by_source(db),
|
||||||
Container::Module(it) => it.id.child_from_source(db, child_source),
|
Container::Module(it) => it.id.child_by_source(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ log = "0.4.5"
|
||||||
once_cell = "1.0.1"
|
once_cell = "1.0.1"
|
||||||
rustc-hash = "1.0"
|
rustc-hash = "1.0"
|
||||||
either = "1.5"
|
either = "1.5"
|
||||||
|
anymap = "0.12"
|
||||||
|
|
||||||
ra_arena = { path = "../ra_arena" }
|
ra_arena = { path = "../ra_arena" }
|
||||||
ra_db = { path = "../ra_db" }
|
ra_db = { path = "../ra_db" }
|
||||||
|
|
139
crates/ra_hir_def/src/child_by_source.rs
Normal file
139
crates/ra_hir_def/src/child_by_source.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
|
||||||
}
|
|
108
crates/ra_hir_def/src/dyn_map.rs
Normal file
108
crates/ra_hir_def/src/dyn_map.rs
Normal 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) }
|
||||||
|
}
|
||||||
|
}
|
48
crates/ra_hir_def/src/keys.rs
Normal file
48
crates/ra_hir_def/src/keys.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,9 @@ pub mod builtin_type;
|
||||||
pub mod diagnostics;
|
pub mod diagnostics;
|
||||||
pub mod per_ns;
|
pub mod per_ns;
|
||||||
|
|
||||||
|
pub mod dyn_map;
|
||||||
|
pub mod keys;
|
||||||
|
|
||||||
pub mod adt;
|
pub mod adt;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod generics;
|
pub mod generics;
|
||||||
|
@ -30,7 +33,7 @@ mod trace;
|
||||||
pub mod nameres;
|
pub mod nameres;
|
||||||
|
|
||||||
pub mod src;
|
pub mod src;
|
||||||
pub mod child_from_source;
|
pub mod child_by_source;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_db;
|
mod test_db;
|
||||||
|
|
|
@ -11,8 +11,8 @@ use std::fmt::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::BodySourceMap, child_from_source::ChildFromSource, db::DefDatabase, nameres::CrateDefMap,
|
body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, keys,
|
||||||
AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
|
nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
|
||||||
};
|
};
|
||||||
use hir_expand::InFile;
|
use hir_expand::InFile;
|
||||||
use insta::assert_snapshot;
|
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 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 fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
|
||||||
let module = db.module_for_file(pos.file_id);
|
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());
|
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)) {
|
if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
//! FIXME: write short doc here
|
//! 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};
|
use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange};
|
||||||
|
|
||||||
|
@ -43,7 +47,7 @@ impl SyntaxNodePtr {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `SyntaxNodePtr`, but remembers the type of node
|
/// Like `SyntaxNodePtr`, but remembers the type of node
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug)]
|
||||||
pub struct AstPtr<N: AstNode> {
|
pub struct AstPtr<N: AstNode> {
|
||||||
raw: SyntaxNodePtr,
|
raw: SyntaxNodePtr,
|
||||||
_ty: PhantomData<fn() -> N>,
|
_ty: PhantomData<fn() -> N>,
|
||||||
|
@ -64,6 +68,12 @@ impl<N: AstNode> PartialEq for AstPtr<N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N: AstNode> Hash for AstPtr<N> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.raw.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<N: AstNode> AstPtr<N> {
|
impl<N: AstNode> AstPtr<N> {
|
||||||
pub fn new(node: &N) -> AstPtr<N> {
|
pub fn new(node: &N) -> AstPtr<N> {
|
||||||
AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData }
|
AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData }
|
||||||
|
|
Loading…
Reference in a new issue