diff --git a/crates/hir-analysis/src/name_resolution/import_resolver.rs b/crates/hir-analysis/src/name_resolution/import_resolver.rs index c402452bc..207e8ba56 100644 --- a/crates/hir-analysis/src/name_resolution/import_resolver.rs +++ b/crates/hir-analysis/src/name_resolution/import_resolver.rs @@ -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(), diff --git a/crates/hir-analysis/src/name_resolution/name_resolver.rs b/crates/hir-analysis/src/name_resolution/name_resolver.rs index b3c0f0687..1c30b1d03 100644 --- a/crates/hir-analysis/src/name_resolution/name_resolver.rs +++ b/crates/hir-analysis/src/name_resolution/name_resolver.rs @@ -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, }; @@ -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::(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::(db.as_hir_db()).is_some() + || scope.resolve_to::(db.as_hir_db()).is_some() + } } } diff --git a/crates/hir/src/hir_def/item.rs b/crates/hir/src/hir_def/item.rs index 535de254f..75fffeeb1 100644 --- a/crates/hir/src/hir_def/item.rs +++ b/crates/hir/src/hir_def/item.rs @@ -82,6 +82,24 @@ impl<'db> ItemKind<'db> { } } + /// Returns attributes being applied to this item. + pub fn attrs(self, db: &'db dyn HirDb) -> Option> { + 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 { @@ -941,6 +959,7 @@ pub struct Const<'db> { id: TrackedItemId<'db>, pub name: Partial>, + pub attributes: AttrListId<'db>, pub ty: Partial>, pub body: Partial>, pub vis: Visibility, diff --git a/crates/hir/src/hir_def/scope_graph.rs b/crates/hir/src/hir_def/scope_graph.rs index 32c3d95c2..484f369be 100644 --- a/crates/hir/src/hir_def/scope_graph.rs +++ b/crates/hir/src/hir_def/scope_graph.rs @@ -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}, @@ -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(self, db: &'db dyn HirDb) -> Option + 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> { + 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) @@ -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); @@ -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> { let mut parent = self.parent(db)?; @@ -268,27 +281,12 @@ 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 { @@ -296,11 +294,9 @@ impl<'db> ScopeId<'db> { } } - 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, @@ -366,6 +362,7 @@ impl<'db> ScopeId<'db> { ScopeId::Block(_, _) => "block", } } + pub fn pretty_path(self, db: &dyn HirDb) -> Option { let name = match self { ScopeId::Block(body, expr) => format!("{{block{}}}", body.iter_block(db)[&expr]), @@ -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; +} + +impl<'db> FromScope<'db> for ItemKind<'db> { + fn from_scope(scope: ScopeId<'db>, _db: &'db dyn HirDb) -> Option { + 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 { + scope.resolve_to::(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 { + 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 { + 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 { + let ScopeId::FuncParam(parent, idx) = scope else { + return None; + }; + + let func: Func = parent.try_into().unwrap(); + func.params(db).to_opt().map(|params| ¶ms.data(db)[idx]) + } +} + +impl<'db> FromScope<'db> for &'db GenericParam<'db> { + fn from_scope(scope: ScopeId<'db>, db: &'db dyn HirDb) -> Option { + 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 { diff --git a/crates/hir/src/lower/item.rs b/crates/hir/src/lower/item.rs index 222ef8bc3..95bb764fc 100644 --- a/crates/hir/src/lower/item.rs +++ b/crates/hir/src/lower/item.rs @@ -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_) } } diff --git a/crates/hir/src/span/item.rs b/crates/hir/src/span/item.rs index 395447362..9f9b4ff52 100644 --- a/crates/hir/src/span/item.rs +++ b/crates/hir/src/span/item.rs @@ -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)) @@ -326,6 +320,7 @@ define_lazy_span_node!( (name, name), } @node { + (attributes, attr_list, LazyAttrListSpan), (ty, ty, LazyTySpan), } ); @@ -346,6 +341,7 @@ define_lazy_span_node!( } @node { (fields, fields, LazyFieldDefListSpan), + (attributes, attr_list, LazyAttrListSpan), (tuple_type, tuple_type, LazyTupleTypeSpan), } );