Skip to content

Commit

Permalink
Add resolution from ScopeId to a hir node
Browse files Browse the repository at this point in the history
  • Loading branch information
Y-Nak committed Jan 17, 2025
1 parent 13033c4 commit e671692
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 54 deletions.
2 changes: 1 addition & 1 deletion crates/hir-analysis/src/name_resolution/import_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@ impl<'db> IntermediateUse<'db> {
}
};

if next_res.is_mod() || next_res.is_enum() {
if next_res.is_mod(db) || next_res.is_enum(db) {
Ok(Self {
use_: self.use_,
current_res: next_res.into(),
Expand Down
13 changes: 8 additions & 5 deletions crates/hir-analysis/src/name_resolution/name_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use hir::{
AnonEdge, EdgeKind, FieldEdge, GenericParamEdge, IngotEdge, LexEdge, ModEdge, ScopeId,
SelfEdge, SelfTyEdge, SuperEdge, TraitEdge, TypeEdge, ValueEdge, VariantEdge,
},
GenericParam, GenericParamOwner, IdentId, ItemKind, Trait, Use,
Enum, GenericParam, GenericParamOwner, IdentId, ItemKind, Mod, TopLevelMod, Trait, Use,
},
span::DynLazySpan,
};
Expand Down Expand Up @@ -306,17 +306,20 @@ impl<'db> NameRes<'db> {
self.trait_().is_some()
}

pub fn is_enum(&self) -> bool {
pub fn is_enum(&self, db: &dyn HirAnalysisDb) -> bool {
match self.kind {
NameResKind::Prim(_) => false,
NameResKind::Scope(scope) => scope.is_enum(),
NameResKind::Scope(scope) => scope.resolve_to::<Enum>(db.as_hir_db()).is_some(),
}
}

pub fn is_mod(&self) -> bool {
pub fn is_mod(&self, db: &dyn HirAnalysisDb) -> bool {
match self.kind {
NameResKind::Prim(_) => false,
NameResKind::Scope(scope) => scope.is_mod(),
NameResKind::Scope(scope) => {
scope.resolve_to::<TopLevelMod>(db.as_hir_db()).is_some()
|| scope.resolve_to::<Mod>(db.as_hir_db()).is_some()
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions crates/hir/src/hir_def/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ impl<'db> ItemKind<'db> {
}
}

/// Returns attributes being applied to this item.
pub fn attrs(self, db: &'db dyn HirDb) -> Option<AttrListId<'db>> {
match self {
Self::Mod(mod_) => mod_.attributes(db),
Self::Func(func) => func.attributes(db),
Self::Struct(struct_) => struct_.attributes(db),
Self::Contract(contract) => contract.attributes(db),
Self::Enum(enum_) => enum_.attributes(db),
Self::TypeAlias(alias) => alias.attributes(db),
Self::Impl(impl_) => impl_.attributes(db),
Self::Trait(trait_) => trait_.attributes(db),
Self::ImplTrait(impl_trait) => impl_trait.attributes(db),
Self::Const(const_) => const_.attributes(db),
_ => return None,
}
.into()
}

pub fn kind_name(self) -> &'static str {
use ItemKind::*;
match self {
Expand Down Expand Up @@ -941,6 +959,7 @@ pub struct Const<'db> {
id: TrackedItemId<'db>,

pub name: Partial<IdentId<'db>>,
pub attributes: AttrListId<'db>,
pub ty: Partial<TypeId<'db>>,
pub body: Partial<Body<'db>>,
pub vis: Visibility,
Expand Down
174 changes: 134 additions & 40 deletions crates/hir/src/hir_def/scope_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use common::indexmap::IndexSet;
use rustc_hash::{FxHashMap, FxHashSet};

use super::{
scope_graph_viz::ScopeGraphFormatter, Body, Enum, ExprId, Func, FuncParamName, IdentId,
IngotId, ItemKind, TopLevelMod, Use, VariantKind, Visibility,
scope_graph_viz::ScopeGraphFormatter, AttrListId, Body, Const, Contract, Enum, ExprId,
FieldDef, Func, FuncParam, FuncParamName, GenericParam, IdentId, Impl, ImplTrait, IngotId,
ItemKind, Mod, TopLevelMod, Trait, TypeAlias, Use, VariantDef, VariantKind, Visibility,
};
use crate::{
hir_def::{BodyKind, GenericParamOwner},
Expand Down Expand Up @@ -130,6 +131,31 @@ impl<'db> ScopeId<'db> {
}
}

/// Resolves the `ScopeId` to `T`.
/// Returns `None` if the resolution is not defined for this scope.
pub fn resolve_to<T>(self, db: &'db dyn HirDb) -> Option<T>
where
T: FromScope<'db>,
{
T::from_scope(self, db)
}

/// Returns attributes being applied to the scope.
pub fn attrs(self, db: &'db dyn HirDb) -> Option<AttrListId<'db>> {
match self {
ScopeId::Item(item) => item.attrs(db),
ScopeId::Field(..) => {
let def: &FieldDef = self.resolve_to(db).unwrap();
Some(def.attributes)
}
ScopeId::Variant(..) => {
let def: &VariantDef = self.resolve_to(db).unwrap();
Some(def.attributes)
}
_ => None,
}
}

/// Returns the scope graph containing this scope.
pub fn scope_graph(self, db: &'db dyn HirDb) -> &'db ScopeGraph<'db> {
self.top_mod(db).scope_graph(db)
Expand Down Expand Up @@ -157,7 +183,7 @@ impl<'db> ScopeId<'db> {
}
}

/// Returns true if `self` is a transitive reflexive child of `of`.
/// Returns `true` if `self` is a transitive reflexive child of `of`.
pub fn is_transitive_child_of(self, db: &dyn HirDb, of: ScopeId) -> bool {
let mut current = Some(self);

Expand Down Expand Up @@ -238,19 +264,6 @@ impl<'db> ScopeId<'db> {
}
}

pub fn is_enum(self) -> bool {
matches!(self, ScopeId::Item(ItemKind::Enum(_)))
}

pub fn is_mod(self) -> bool {
matches!(self, ScopeId::Item(ItemKind::Mod(_) | ItemKind::TopMod(_)))
}

/// Returns `true` if the scope is a trait definition.
pub fn is_trait(self) -> bool {
matches!(self, ScopeId::Item(ItemKind::Trait(_)))
}

/// Returns the item that contains this scope.
pub fn parent_item(self, db: &'db dyn HirDb) -> Option<ItemKind<'db>> {
let mut parent = self.parent(db)?;
Expand All @@ -268,39 +281,22 @@ impl<'db> ScopeId<'db> {
match self.data(db).id {
ScopeId::Item(item) => item.name(db),

ScopeId::Variant(parent, idx) => {
let enum_: Enum = parent.try_into().unwrap();
enum_.variants(db).data(db)[idx].name.to_opt()
}
ScopeId::Variant(..) => self.resolve_to::<&VariantDef>(db).unwrap().name.to_opt(),

ScopeId::Field(FieldParent::Item(parent), idx) => match parent {
ItemKind::Struct(s) => s.fields(db).data(db)[idx].name.to_opt(),
ItemKind::Contract(c) => c.fields(db).data(db)[idx].name.to_opt(),
_ => unreachable!(),
},
ScopeId::Field(FieldParent::Variant(parent, vidx), fidx) => {
let enum_: Enum = parent.try_into().unwrap();
match enum_.variants(db).data(db)[vidx].kind {
VariantKind::Record(fields) => fields.data(db)[fidx].name.to_opt(),
_ => unreachable!(),
}
}
ScopeId::Field(..) => self.resolve_to::<&FieldDef>(db).unwrap().name.to_opt(),

ScopeId::FuncParam(parent, idx) => {
let func: Func = parent.try_into().unwrap();
let param = &func.params(db).to_opt()?.data(db)[idx];
ScopeId::FuncParam(..) => {
let param: &FuncParam = self.resolve_to(db).unwrap();
if let Some(FuncParamName::Ident(ident)) = param.label {
Some(ident)
} else {
param.name()
}
}

ScopeId::GenericParam(parent, idx) => {
let parent = GenericParamOwner::from_item_opt(parent).unwrap();

let params = &parent.params(db).data(db)[idx];
params.name().to_opt()
ScopeId::GenericParam(..) => {
let param: &GenericParam = self.resolve_to(db).unwrap();
param.name().to_opt()
}

ScopeId::Block(..) => None,
Expand Down Expand Up @@ -366,6 +362,7 @@ impl<'db> ScopeId<'db> {
ScopeId::Block(_, _) => "block",
}
}

pub fn pretty_path(self, db: &dyn HirDb) -> Option<String> {
let name = match self {
ScopeId::Block(body, expr) => format!("{{block{}}}", body.iter_block(db)[&expr]),
Expand Down Expand Up @@ -594,6 +591,103 @@ pub struct SelfEdge();
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AnonEdge();

pub trait FromScope<'db>: Sized {
fn from_scope(scope: ScopeId<'db>, db: &'db dyn HirDb) -> Option<Self>;
}

impl<'db> FromScope<'db> for ItemKind<'db> {
fn from_scope(scope: ScopeId<'db>, _db: &'db dyn HirDb) -> Option<Self> {
match scope {
ScopeId::Item(item) => Some(item),
_ => None,
}
}
}

macro_rules! item_from_scope {
($($item_ty: ty,)*) => {
$(
impl<'db> FromScope<'db> for $item_ty {
fn from_scope(scope: ScopeId<'db>, db: &'db dyn HirDb) -> Option<Self> {
scope.resolve_to::<ItemKind>(db).and_then(|item| item.try_into().ok())
}
}
)*
};
}

item_from_scope! {
TopLevelMod<'db>,
Mod<'db>,
Func<'db>,
Contract<'db>,
Enum<'db>,
TypeAlias<'db>,
Impl<'db>,
Trait<'db>,
ImplTrait<'db>,
Const<'db>,
Use<'db>,
Body<'db>,
}

impl<'db> FromScope<'db> for &'db FieldDef<'db> {
fn from_scope(scope: ScopeId<'db>, db: &'db dyn HirDb) -> Option<Self> {
let ScopeId::Field(parent, idx) = scope else {
return None;
};

match parent {
FieldParent::Item(item) => match item {
ItemKind::Struct(s) => Some(&s.fields(db).data(db)[idx]),
ItemKind::Contract(c) => Some(&c.fields(db).data(db)[idx]),
_ => unreachable!(),
},

FieldParent::Variant(parent, vidx) => {
let enum_: Enum = parent.try_into().unwrap();
match enum_.variants(db).data(db)[vidx].kind {
VariantKind::Record(fields) => Some(&fields.data(db)[idx]),
_ => unreachable!(),
}
}
}
}
}

impl<'db> FromScope<'db> for &'db VariantDef<'db> {
fn from_scope(scope: ScopeId<'db>, db: &'db dyn HirDb) -> Option<Self> {
let ScopeId::Variant(parent, idx) = scope else {
return None;
};
let enum_: Enum = parent.try_into().unwrap();

Some(&enum_.variants(db).data(db)[idx])
}
}

impl<'db> FromScope<'db> for &'db FuncParam<'db> {
fn from_scope(scope: ScopeId<'db>, db: &'db dyn HirDb) -> Option<Self> {
let ScopeId::FuncParam(parent, idx) = scope else {
return None;
};

let func: Func = parent.try_into().unwrap();
func.params(db).to_opt().map(|params| &params.data(db)[idx])
}
}

impl<'db> FromScope<'db> for &'db GenericParam<'db> {
fn from_scope(scope: ScopeId<'db>, db: &'db dyn HirDb) -> Option<Self> {
let ScopeId::GenericParam(parent, idx) = scope else {
return None;
};

let parent = GenericParamOwner::from_item_opt(parent).unwrap();
Some(&parent.params(db).data(db)[idx])
}
}

#[cfg(test)]
mod tests {

Expand Down
13 changes: 12 additions & 1 deletion crates/hir/src/lower/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,12 +350,23 @@ impl<'db> Const<'db> {
let id = ctxt.joined_id(TrackedItemVariant::Const(name));
ctxt.enter_item_scope(id, false);

let attributes = AttrListId::lower_ast_opt(ctxt, ast.attr_list());
let ty = TypeId::lower_ast_partial(ctxt, ast.ty());
let body = ast.value().map(|ast| Body::lower_ast(ctxt, ast)).into();
let vis = ItemModifier::lower_ast(ast.modifier()).to_visibility();
let origin = HirOrigin::raw(&ast);

let const_ = Self::new(ctxt.db(), id, name, ty, body, vis, ctxt.top_mod(), origin);
let const_ = Self::new(
ctxt.db(),
id,
name,
attributes,
ty,
body,
vis,
ctxt.top_mod(),
origin,
);
ctxt.leave_item_scope(const_)
}
}
Expand Down
10 changes: 3 additions & 7 deletions crates/hir/src/span/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,7 @@ impl<'db> LazyConstSpan<'db> {
}
}

define_lazy_span_node!(
LazyUseSpan,
ast::Use,
@node {
(attributes, attr_list, LazyAttrListSpan),
}
);
define_lazy_span_node!(LazyUseSpan, ast::Use);
impl<'db> LazyUseSpan<'db> {
pub fn new(u: Use<'db>) -> Self {
Self(crate::span::transition::SpanTransitionChain::new(u))
Expand Down Expand Up @@ -326,6 +320,7 @@ define_lazy_span_node!(
(name, name),
}
@node {
(attributes, attr_list, LazyAttrListSpan),
(ty, ty, LazyTySpan),
}
);
Expand All @@ -346,6 +341,7 @@ define_lazy_span_node!(
}
@node {
(fields, fields, LazyFieldDefListSpan),
(attributes, attr_list, LazyAttrListSpan),
(tuple_type, tuple_type, LazyTupleTypeSpan),
}
);
Expand Down

0 comments on commit e671692

Please sign in to comment.