Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/semantics.rs')
-rw-r--r--crates/hir/src/semantics.rs201
1 files changed, 157 insertions, 44 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 4fb449117d..889b0e05af 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -13,8 +13,8 @@ use std::{
use base_db::{FxIndexSet, all_crates, toolchain_channel};
use either::Either;
use hir_def::{
- BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, HasModule, MacroId, StructId,
- TraitId, VariantId,
+ BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, HasModule, MacroId,
+ StructId, TraitId, VariantId,
attrs::parse_extra_crate_attrs,
expr_store::{Body, ExprOrPatSource, ExpressionStore, HygieneId, path::Path},
hir::{BindingId, Expr, ExprId, ExprOrPatId},
@@ -32,7 +32,8 @@ use hir_expand::{
name::AsName,
};
use hir_ty::{
- InferenceResult,
+ InferBodyId, InferenceResult,
+ db::AnonConstId,
diagnostics::unsafe_operations,
infer_query_with_inspect,
next_solver::{
@@ -164,11 +165,17 @@ pub struct Semantics<'db, DB: ?Sized> {
imp: SemanticsImpl<'db>,
}
+type DefWithoutBodyWithAnonConsts = Either<GenericDefId, VariantId>;
+type ExprToAnonConst = FxHashMap<ExprId, AnonConstId>;
+type DefAnonConstsMap = FxHashMap<DefWithoutBodyWithAnonConsts, ExprToAnonConst>;
+
pub struct SemanticsImpl<'db> {
pub db: &'db dyn HirDatabase,
s2d_cache: RefCell<SourceToDefCache<'db>>,
/// MacroCall to its expansion's MacroCallId cache
macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroCallId>>,
+ /// All anon consts defined by a *signature* (not a body).
+ signature_anon_consts_cache: RefCell<DefAnonConstsMap>,
}
impl<DB: ?Sized> fmt::Debug for Semantics<'_, DB> {
@@ -454,7 +461,12 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
impl<'db> SemanticsImpl<'db> {
fn new(db: &'db dyn HirDatabase) -> Self {
- SemanticsImpl { db, s2d_cache: Default::default(), macro_call_cache: Default::default() }
+ SemanticsImpl {
+ db,
+ s2d_cache: Default::default(),
+ macro_call_cache: Default::default(),
+ signature_anon_consts_cache: Default::default(),
+ }
}
pub fn parse(&self, file_id: EditionedFileId) -> ast::SourceFile {
@@ -788,21 +800,16 @@ impl<'db> SemanticsImpl<'db> {
/// Checks if renaming `renamed` to `new_name` may introduce conflicts with other locals,
/// and returns the conflicting locals.
pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> {
- // FIXME: signatures
- let Some(def) = to_be_renamed.parent.as_def_with_body() else {
- return Vec::new();
- };
- let body = Body::of(self.db, def);
+ let (store, root_expr) = to_be_renamed.parent_infer.store_and_root_expr(self.db);
let resolver = to_be_renamed.parent.resolver(self.db);
- let starting_expr =
- body.binding_owner(to_be_renamed.binding_id).unwrap_or(body.root_expr());
+ let starting_expr = store.binding_owner(to_be_renamed.binding_id).unwrap_or(root_expr);
let mut visitor = RenameConflictsVisitor {
- body,
+ body: store,
conflicts: FxHashSet::default(),
db: self.db,
new_name: new_name.symbol().clone(),
old_name: to_be_renamed.name(self.db).symbol().clone(),
- owner: def,
+ owner: to_be_renamed.parent,
to_be_renamed: to_be_renamed.binding_id,
resolver,
};
@@ -810,7 +817,11 @@ impl<'db> SemanticsImpl<'db> {
visitor
.conflicts
.into_iter()
- .map(|binding_id| Local { parent: to_be_renamed.parent, binding_id })
+ .map(|binding_id| Local {
+ parent: to_be_renamed.parent,
+ parent_infer: to_be_renamed.parent_infer,
+ binding_id,
+ })
.collect()
}
@@ -1960,15 +1971,16 @@ impl<'db> SemanticsImpl<'db> {
pub fn get_unsafe_ops(&self, def: ExpressionStoreOwner) -> FxHashSet<ExprOrPatSource> {
let Ok(def) = ExpressionStoreOwnerId::try_from(def) else { return Default::default() };
let (body, source_map) = ExpressionStore::with_source_map(self.db, def);
- let infer = InferenceResult::of(self.db, def);
let mut res = FxHashSet::default();
- for root in body.expr_roots() {
- unsafe_operations(self.db, infer, def, body, root, &mut |node, _| {
- if let Ok(node) = source_map.expr_or_pat_syntax(node) {
- res.insert(node);
- }
- });
- }
+ self.with_all_infers_for_store(def, &mut |infer| {
+ for root in body.expr_roots() {
+ unsafe_operations(self.db, infer, def, body, root, &mut |node, _| {
+ if let Ok(node) = source_map.expr_or_pat_syntax(node) {
+ res.insert(node);
+ }
+ });
+ }
+ });
res
}
@@ -2116,11 +2128,14 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
- self.analyze_no_infer(node).map(|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
- db: self.db,
- file_id,
- resolver,
- })
+ self.analyze_no_infer(node).map(
+ |SourceAnalyzer { file_id, resolver, infer_body, .. }| SemanticsScope {
+ db: self.db,
+ file_id,
+ resolver,
+ infer_body,
+ },
+ )
}
pub fn scope_at_offset(
@@ -2129,10 +2144,11 @@ impl<'db> SemanticsImpl<'db> {
offset: TextSize,
) -> Option<SemanticsScope<'db>> {
self.analyze_with_offset_no_infer(node, offset).map(
- |SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
+ |SourceAnalyzer { file_id, resolver, infer_body, .. }| SemanticsScope {
db: self.db,
file_id,
resolver,
+ infer_body,
},
)
}
@@ -2159,6 +2175,85 @@ impl<'db> SemanticsImpl<'db> {
container.as_expression_store_owner().map(|id| id.into())
}
+ fn populate_anon_const_cache_for<'a>(
+ &self,
+ cache: &'a mut DefAnonConstsMap,
+ def: DefWithoutBodyWithAnonConsts,
+ ) -> &'a ExprToAnonConst {
+ cache.entry(def).or_insert_with(|| match def {
+ Either::Left(def) => {
+ let all_anon_consts =
+ AnonConstId::all_from_signature(self.db, def).into_iter().flatten().copied();
+ all_anon_consts
+ .map(|anon_const| (anon_const.loc(self.db).expr, anon_const))
+ .collect()
+ }
+ Either::Right(def) => {
+ let all_anon_consts =
+ self.db.field_types_with_diagnostics(def).defined_anon_consts().iter().copied();
+ all_anon_consts
+ .map(|anon_const| (anon_const.loc(self.db).expr, anon_const))
+ .collect()
+ }
+ })
+ }
+
+ fn find_anon_const_for_root_expr_in_signature(
+ &self,
+ def: DefWithoutBodyWithAnonConsts,
+ root_expr: ExprId,
+ ) -> Option<AnonConstId> {
+ let mut cache = self.signature_anon_consts_cache.borrow_mut();
+ let anon_consts_map = self.populate_anon_const_cache_for(&mut cache, def);
+ anon_consts_map.get(&root_expr).copied()
+ }
+
+ pub(crate) fn infer_body_for_expr_or_pat(
+ &self,
+ def: ExpressionStoreOwnerId,
+ store: &ExpressionStore,
+ node: ExprOrPatId,
+ ) -> Option<InferBodyId> {
+ let handle_def_without_body = |def| {
+ let root_expr = match node {
+ ExprOrPatId::ExprId(expr) => store.find_root_for_expr(expr),
+ ExprOrPatId::PatId(pat) => store.find_root_for_pat(pat),
+ };
+ let anon_const = self.find_anon_const_for_root_expr_in_signature(def, root_expr)?;
+ Some(anon_const.into())
+ };
+ match def {
+ ExpressionStoreOwnerId::Signature(def) => handle_def_without_body(Either::Left(def)),
+ ExpressionStoreOwnerId::Body(def) => Some(def.into()),
+ ExpressionStoreOwnerId::VariantFields(def) => {
+ handle_def_without_body(Either::Right(def))
+ }
+ }
+ }
+
+ fn with_all_infers_for_store(
+ &self,
+ owner: ExpressionStoreOwnerId,
+ callback: &mut dyn FnMut(&'db InferenceResult),
+ ) {
+ let mut handle_def_without_body = |def| {
+ let mut cache = self.signature_anon_consts_cache.borrow_mut();
+ let map = self.populate_anon_const_cache_for(&mut cache, def);
+ for &anon_const in map.values() {
+ callback(InferenceResult::of(self.db, anon_const));
+ }
+ };
+ match owner {
+ ExpressionStoreOwnerId::Signature(def) => handle_def_without_body(Either::Left(def)),
+ ExpressionStoreOwnerId::Body(def) => {
+ callback(InferenceResult::of(self.db, def));
+ }
+ ExpressionStoreOwnerId::VariantFields(def) => {
+ handle_def_without_body(Either::Right(def))
+ }
+ }
+ }
+
/// Returns none if the file of the node is not part of a crate.
fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer<'db>> {
let node = self.find_file(node);
@@ -2200,34 +2295,36 @@ impl<'db> SemanticsImpl<'db> {
});
}
ChildContainer::VariantId(def) => {
- return Some(SourceAnalyzer::new_variant_body(self.db, def, node, offset, infer));
+ return Some(SourceAnalyzer::new_variant_body(
+ self.db, self, def, node, offset, infer,
+ ));
}
ChildContainer::TraitId(it) => {
return Some(if infer {
- SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset)
} else {
- SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset)
});
}
ChildContainer::ImplId(it) => {
return Some(if infer {
- SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset)
} else {
- SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset)
});
}
ChildContainer::EnumId(it) => {
return Some(if infer {
- SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset)
} else {
- SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset)
});
}
ChildContainer::GenericDefId(it) => {
return Some(if infer {
- SourceAnalyzer::new_generic_def(self.db, it, node, offset)
+ SourceAnalyzer::new_generic_def(self.db, self, it, node, offset)
} else {
- SourceAnalyzer::new_generic_def_no_infer(self.db, it, node, offset)
+ SourceAnalyzer::new_generic_def_no_infer(self.db, self, it, node, offset)
});
}
ChildContainer::ModuleId(it) => it.resolver(self.db),
@@ -2360,6 +2457,7 @@ impl<'db> SemanticsImpl<'db> {
text_range: TextRange,
) -> Option<FxIndexSet<Local>> {
let sa = self.analyze(element.either(|e| e.syntax(), |s| s.syntax()))?;
+ let infer_body = sa.infer_body?;
let store = sa.store()?;
let mut resolver = sa.resolver.clone();
let def = resolver.expression_store_owner()?;
@@ -2426,7 +2524,11 @@ impl<'db> SemanticsImpl<'db> {
let hygiene = store.expr_or_pat_path_hygiene(id);
resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).inspect(|value| {
if let ValueNs::LocalBinding(id) = value {
- locals.insert((def, *id).into());
+ locals.insert(Local {
+ parent: def,
+ parent_infer: infer_body,
+ binding_id: *id,
+ });
}
});
}
@@ -2554,7 +2656,6 @@ to_def_impls![
(crate::ConstParam, ast::ConstParam, const_param_to_def),
(crate::GenericParam, ast::GenericParam, generic_param_to_def),
(crate::Macro, ast::Macro, macro_to_def),
- (crate::Local, ast::IdentPat, bind_pat_to_def),
(crate::Local, ast::SelfParam, self_param_to_def),
(crate::Label, ast::Label, label_to_def),
(crate::Adt, ast::Adt, adt_to_def),
@@ -2564,6 +2665,14 @@ to_def_impls![
(MacroCallId, ast::MacroCall, macro_call_to_macro_call),
];
+impl ToDef for ast::IdentPat {
+ type Def = crate::Local;
+
+ fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def> {
+ sema.with_ctx(|ctx| ctx.bind_pat_to_def(src, sema))
+ }
+}
+
fn find_root(node: &SyntaxNode) -> SyntaxNode {
node.ancestors().last().unwrap()
}
@@ -2590,6 +2699,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
#[derive(Debug)]
pub struct SemanticsScope<'db> {
pub db: &'db dyn HirDatabase,
+ infer_body: Option<InferBodyId>,
file_id: HirFileId,
resolver: Resolver<'db>,
}
@@ -2641,9 +2751,11 @@ impl<'db> SemanticsScope<'db> {
resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()),
resolver::ScopeDef::Local(binding_id) => {
- match self.resolver.expression_store_owner() {
- Some(parent) => ScopeDef::Local(Local { parent, binding_id }),
- None => continue,
+ match (self.resolver.expression_store_owner(), self.infer_body) {
+ (Some(parent), Some(parent_infer)) => {
+ ScopeDef::Local(Local { parent, parent_infer, binding_id })
+ }
+ _ => continue,
}
}
resolver::ScopeDef::Label(label_id) => {
@@ -2697,6 +2809,7 @@ impl<'db> SemanticsScope<'db> {
resolve_hir_path(
self.db,
&self.resolver,
+ self.infer_body,
&Path::BarePath(Interned::new(ModPath::from_segments(kind, segments))),
HygieneId::ROOT,
None,
@@ -2755,9 +2868,9 @@ impl ops::Deref for VisibleTraits {
struct RenameConflictsVisitor<'a> {
db: &'a dyn HirDatabase,
- owner: DefWithBodyId,
+ owner: ExpressionStoreOwnerId,
resolver: Resolver<'a>,
- body: &'a Body,
+ body: &'a ExpressionStore,
to_be_renamed: BindingId,
new_name: Symbol,
old_name: Symbol,