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.rs | 290 |
1 files changed, 217 insertions, 73 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index b7cc780ae4..a1bbe47188 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -13,11 +13,11 @@ 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, Pat}, + hir::{BindingId, Expr, ExprId, ExprOrPatId}, nameres::{ModuleOrigin, crate_def_map}, resolver::{self, HasResolver, Resolver, TypeNs, ValueNs}, type_ref::Mutability, @@ -32,24 +32,24 @@ use hir_expand::{ name::AsName, }; use hir_ty::{ - InferenceResult, + InferBodyId, InferenceResult, + db::AnonConstId, diagnostics::unsafe_operations, infer_query_with_inspect, next_solver::{ - AnyImplId, DbInterner, Span, + AnyImplId, DbInterner, format_proof_tree::{ProofTreeData, dump_proof_tree_structured}, }, }; use intern::{Interned, Symbol, sym}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::Span as _; use smallvec::{SmallVec, smallvec}; use span::{FileId, SyntaxContext}; use stdx::{TupleExt, always}; use syntax::{ - AstNode, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, SyntaxNode, - SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize, + AstNode, AstPtr, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, + SyntaxNode, SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize, algo::skip_trivia_token, ast::{self, HasAttrs as _, HasGenericParams}, }; @@ -165,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>, + 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> { @@ -455,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 { @@ -519,7 +530,7 @@ impl<'db> SemanticsImpl<'db> { } } HirFileId::MacroFile(macro_file) => { - let node = self.db.lookup_intern_macro_call(macro_file).to_node(self.db); + let node = macro_file.loc(self.db).to_node(self.db); let root = find_root(&node.value); self.cache(root, node.file_id); Some(node) @@ -544,8 +555,16 @@ impl<'db> SemanticsImpl<'db> { node } + pub fn to_node_syntax(&self, ptr: InFile<SyntaxNodePtr>) -> SyntaxNode { + ptr.value.to_node(&self.parse_or_expand(ptr.file_id)) + } + + pub fn to_node<N: AstNode>(&self, ptr: InFile<AstPtr<N>>) -> N { + ptr.value.to_node(&self.parse_or_expand(ptr.file_id)) + } + pub fn expand(&self, file_id: MacroCallId) -> ExpandResult<SyntaxNode> { - let res = self.db.parse_macro_expansion(file_id).map(|it| it.0.syntax_node()); + let res = self.db.parse_macro_expansion(file_id).as_ref().map(|it| it.0.syntax_node()); self.cache(res.value.clone(), file_id.into()); res } @@ -563,7 +582,7 @@ impl<'db> SemanticsImpl<'db> { macro_call: &ast::MacroCall, ) -> Option<ExpandResult<SyntaxNode>> { let file_id = self.to_def(macro_call)?; - let macro_call = self.db.lookup_intern_macro_call(file_id); + let macro_call = file_id.loc(self.db); let skip = matches!( macro_call.def.kind, @@ -645,7 +664,7 @@ impl<'db> SemanticsImpl<'db> { let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id); let root_node = value.0.syntax_node(); self.cache(root_node.clone(), file_id.into()); - Some(ExpandResult { value: root_node, err }) + Some(ExpandResult { value: root_node, err: err.clone() }) }) .collect(); Some(res) @@ -781,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, }; @@ -803,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() } @@ -1304,7 +1322,7 @@ impl<'db> SemanticsImpl<'db> { }) .map(|(call_id, item)| { let item_range = item.syntax().text_range(); - let loc = db.lookup_intern_macro_call(call_id); + let loc = call_id.loc(db); let text_range = match loc.kind { hir_expand::MacroCallKind::Attr { censored_attr_ids: attr_ids, @@ -1635,6 +1653,44 @@ impl<'db> SemanticsImpl<'db> { .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) } + /// Returns the `return` expressions in this function's body, + /// excluding those inside closures or async blocks. + pub fn fn_return_points(&self, func: Function) -> Vec<InFile<ast::ReturnExpr>> { + let func_id = match func.id { + AnyFunctionId::FunctionId(id) => id, + _ => return vec![], + }; + let (body, source_map) = Body::with_source_map(self.db, func_id.into()); + + fn collect_returns( + sema: &SemanticsImpl<'_>, + body: &Body, + source_map: &hir_def::expr_store::ExpressionStoreSourceMap, + expr_id: ExprId, + acc: &mut Vec<InFile<ast::ReturnExpr>>, + ) { + match &body[expr_id] { + Expr::Closure { .. } | Expr::Const(_) => return, + Expr::Return { .. } => { + if let Ok(source) = source_map.expr_syntax(expr_id) + && let Some(ret_expr) = source.value.cast::<ast::ReturnExpr>() + { + let root = sema.parse_or_expand(source.file_id); + acc.push(InFile::new(source.file_id, ret_expr.to_node(&root))); + } + } + _ => {} + } + body.walk_child_exprs(expr_id, |child| { + collect_returns(sema, body, source_map, child, acc); + }); + } + + let mut returns = vec![]; + collect_returns(self, body, source_map, body.root_expr(), &mut returns); + returns + } + pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> { let text = lifetime.text(); let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| { @@ -1915,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 } @@ -2071,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( @@ -2084,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, }, ) } @@ -2114,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); @@ -2155,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), @@ -2315,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()?; @@ -2371,13 +2514,7 @@ impl<'db> SemanticsImpl<'db> { None } } - ExprOrPatId::PatId(pat_id) => { - if let Pat::Path(path) = &store[pat_id] { - Some(path) - } else { - None - } - } + ExprOrPatId::PatId(_) => None, }; if let Some(path) = path @@ -2387,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, + }); } }); } @@ -2427,7 +2568,8 @@ impl<'db> SemanticsImpl<'db> { if result.is_err() && let Some(tree) = proof_tree { - let data = dump_proof_tree_structured(tree, Span::dummy(), infer_ctxt); + let data = + dump_proof_tree_structured(tree, hir_ty::Span::Dummy, infer_ctxt); RESULT.with(|ctx| ctx.borrow_mut().push(data)); } }), @@ -2448,7 +2590,7 @@ fn macro_call_to_macro_id( macro_call_id: MacroCallId, ) -> Option<MacroId> { let db: &dyn ExpandDatabase = ctx.db; - let loc = db.lookup_intern_macro_call(macro_call_id); + let loc = macro_call_id.loc(db); match loc.def.ast_id() { Either::Left(it) => { @@ -2514,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), @@ -2524,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() } @@ -2550,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>, } @@ -2601,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) => { @@ -2657,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, @@ -2715,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, @@ -2761,15 +2914,6 @@ impl RenameConflictsVisitor<'_> { self.resolve_path(expr.into(), path); self.resolver.reset_to_guard(guard); } - &Expr::Assignment { target, .. } => { - let guard = self.resolver.update_to_inner_scope(self.db, self.owner, expr); - self.body.walk_pats(target, &mut |pat| { - if let Pat::Path(path) = &self.body[pat] { - self.resolve_path(pat.into(), path); - } - }); - self.resolver.reset_to_guard(guard); - } _ => {} } |