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 | 204 |
1 files changed, 115 insertions, 89 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index adba59236a..62ce3daab7 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -28,13 +28,16 @@ use hir_expand::{ mod_path::{ModPath, PathKind}, name::AsName, }; -use hir_ty::diagnostics::unsafe_operations_for_body; +use hir_ty::{ + diagnostics::{unsafe_operations, unsafe_operations_for_body}, + next_solver::DbInterner, +}; use intern::{Interned, Symbol, sym}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{SmallVec, smallvec}; use span::{Edition, FileId, SyntaxContext}; -use stdx::TupleExt; +use stdx::{TupleExt, always}; use syntax::{ AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, @@ -46,8 +49,8 @@ use crate::{ Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, - Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, - Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TupleField, Type, + TypeAlias, TypeParam, Union, Variant, VariantDef, db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{SourceAnalyzer, name_hygiene, resolve_hir_path}, @@ -85,8 +88,7 @@ impl PathResolution { | ModuleDef::Function(_) | ModuleDef::Module(_) | ModuleDef::Static(_) - | ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_), + | ModuleDef::Trait(_), ) => None, PathResolution::Def(ModuleDef::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) @@ -303,6 +305,13 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> { self.imp.hir_file_to_module_defs(file.into()) } + pub fn is_nightly(&self, krate: Crate) -> bool { + let toolchain = self.db.toolchain_channel(krate.into()); + // `toolchain == None` means we're in some detached files. Since we have no information on + // the toolchain being used, let's just allow unstable items to be listed. + matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None) + } + pub fn to_adt_def(&self, a: &ast::Adt) -> Option<Adt> { self.imp.to_def(a) } @@ -343,10 +352,6 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> { self.imp.to_def(s) } - pub fn to_trait_alias_def(&self, t: &ast::TraitAlias) -> Option<TraitAlias> { - self.imp.to_def(t) - } - pub fn to_trait_def(&self, t: &ast::Trait) -> Option<Trait> { self.imp.to_def(t) } @@ -933,19 +938,18 @@ impl<'db> SemanticsImpl<'db> { InFile::new(file.file_id, last), false, &mut |InFile { value: last, file_id: last_fid }, _ctx| { - if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() { - if first_fid == last_fid { - if let Some(p) = first.parent() { - let range = first.text_range().cover(last.text_range()); - let node = find_root(&p) - .covering_element(range) - .ancestors() - .take_while(|it| it.text_range() == range) - .find_map(N::cast); - if let Some(node) = node { - res.push(node); - } - } + if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() + && first_fid == last_fid + && let Some(p) = first.parent() + { + let range = first.text_range().cover(last.text_range()); + let node = find_root(&p) + .covering_element(range) + .ancestors() + .take_while(|it| it.text_range() == range) + .find_map(N::cast); + if let Some(node) = node { + res.push(node); } } }, @@ -1242,29 +1246,27 @@ impl<'db> SemanticsImpl<'db> { adt, )) })?; - let mut res = None; for (_, derive_attr, derives) in derives { // as there may be multiple derives registering the same helper // name, we gotta make sure to call this for all of them! // FIXME: We need to call `f` for all of them as well though! - res = res.or(process_expansion_for_token( - ctx, - &mut stack, - derive_attr, - )); + process_expansion_for_token(ctx, &mut stack, derive_attr); for derive in derives.into_iter().flatten() { - res = res - .or(process_expansion_for_token(ctx, &mut stack, derive)); + process_expansion_for_token(ctx, &mut stack, derive); } } // remove all tokens that are within the derives expansion filter_duplicates(tokens, adt.syntax().text_range()); - Some(res) + Some(()) }); // if we found derives, we can early exit. There is no way we can be in any // macro call at this point given we are not in a token tree - if let Some(res) = res { - return res; + if let Some(()) = res { + // Note: derives do not remap the original token. Furthermore, we want + // the original token to be before the derives in the list, because if they + // upmap to the same token and we deduplicate them (e.g. in rename), we + // want the original token to remain, not the derive. + return None; } } // Then check for token trees, that means we are either in a function-like macro or @@ -1391,10 +1393,10 @@ impl<'db> SemanticsImpl<'db> { } })() .is_none(); - if was_not_remapped { - if let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) { - return Some(b); - } + if was_not_remapped + && let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) + { + return Some(b); } } } @@ -1554,8 +1556,8 @@ impl<'db> SemanticsImpl<'db> { pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment<'db>>> { let mutability = |m| match m { - hir_ty::Mutability::Not => Mutability::Shared, - hir_ty::Mutability::Mut => Mutability::Mut, + hir_ty::next_solver::Mutability::Not => Mutability::Shared, + hir_ty::next_solver::Mutability::Mut => Mutability::Mut, }; let analyzer = self.analyze(expr.syntax())?; @@ -1566,7 +1568,7 @@ impl<'db> SemanticsImpl<'db> { it.iter() .map(|adjust| { let target = - Type::new_with_resolver(self.db, &analyzer.resolver, adjust.target.clone()); + Type::new_with_resolver(self.db, &analyzer.resolver, adjust.target); let kind = match adjust.kind { hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { @@ -1653,11 +1655,15 @@ impl<'db> SemanticsImpl<'db> { func: Function, subst: impl IntoIterator<Item = Type<'db>>, ) -> Option<Function> { - let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None); - for s in subst { - substs = substs.push(s.ty); - } - Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into()) + let interner = DbInterner::new_with(self.db, None, None); + let mut subst = subst.into_iter(); + let substs = + hir_ty::next_solver::GenericArgs::for_item(interner, trait_.id.into(), |_, id, _| { + assert!(matches!(id, hir_def::GenericParamId::TypeParamId(_)), "expected a type"); + subst.next().expect("too few subst").ty.into() + }); + assert!(subst.next().is_none(), "too many subst"); + Some(self.db.lookup_impl_method(env.env, func.into(), substs).0.into()) } fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> { @@ -1773,6 +1779,25 @@ impl<'db> SemanticsImpl<'db> { res } + pub fn get_unsafe_ops_for_unsafe_block(&self, block: ast::BlockExpr) -> Vec<ExprOrPatSource> { + always!(block.unsafe_token().is_some()); + let block = self.wrap_node_infile(ast::Expr::from(block)); + let Some(def) = self.body_for(block.syntax()) else { return Vec::new() }; + let def = def.into(); + let (body, source_map) = self.db.body_with_source_map(def); + let infer = self.db.infer(def); + let Some(ExprOrPatId::ExprId(block)) = source_map.node_expr(block.as_ref()) else { + return Vec::new(); + }; + let mut res = Vec::default(); + unsafe_operations(self.db, &infer, def, &body, block, &mut |node, _| { + if let Ok(node) = source_map.expr_or_pat_syntax(node) { + res.push(node); + } + }); + res + } + pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { let Some(mac) = self.resolve_macro_call(macro_call) else { return false }; if mac.is_asm_like(self.db) { @@ -2068,14 +2093,12 @@ impl<'db> SemanticsImpl<'db> { break false; } - if let Some(parent) = ast::Expr::cast(parent.clone()) { - if let Some(ExprOrPatId::ExprId(expr_id)) = + if let Some(parent) = ast::Expr::cast(parent.clone()) + && let Some(ExprOrPatId::ExprId(expr_id)) = source_map.node_expr(InFile { file_id, value: &parent }) - { - if let Expr::Unsafe { .. } = body[expr_id] { - break true; - } - } + && let Expr::Unsafe { .. } = body[expr_id] + { + break true; } let Some(parent_) = parent.parent() else { break false }; @@ -2143,7 +2166,6 @@ to_def_impls![ (crate::Enum, ast::Enum, enum_to_def), (crate::Union, ast::Union, union_to_def), (crate::Trait, ast::Trait, trait_to_def), - (crate::TraitAlias, ast::TraitAlias, trait_alias_to_def), (crate::Impl, ast::Impl, impl_to_def), (crate::TypeAlias, ast::TypeAlias, type_alias_to_def), (crate::Const, ast::Const, const_to_def), @@ -2252,6 +2274,11 @@ impl<'db> SemanticsScope<'db> { } } + /// Checks if a trait is in scope, either because of an import or because we're in an impl of it. + pub fn can_use_trait_methods(&self, t: Trait) -> bool { + self.resolver.traits_in_scope(self.db).contains(&t.id) + } + /// Resolve a path as-if it was written at the given scope. This is /// necessary a heuristic, as it doesn't take hygiene into account. pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option<PathResolution> { @@ -2299,18 +2326,19 @@ impl<'db> SemanticsScope<'db> { /// Iterates over associated types that may be specified after the given path (using /// `Ty::Assoc` syntax). - pub fn assoc_type_shorthand_candidates<R>( + pub fn assoc_type_shorthand_candidates( &self, resolution: &PathResolution, - mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>, - ) -> Option<R> { - let def = self.resolver.generic_def()?; - hir_ty::associated_type_shorthand_candidates( - self.db, - def, - resolution.in_type_ns()?, - |name, id| cb(name, id.into()), - ) + mut cb: impl FnMut(TypeAlias), + ) { + let (Some(def), Some(resolution)) = (self.resolver.generic_def(), resolution.in_type_ns()) + else { + return; + }; + hir_ty::associated_type_shorthand_candidates(self.db, def, resolution, |_, id| { + cb(id.into()); + false + }); } pub fn generic_def(&self) -> Option<crate::GenericDef> { @@ -2354,32 +2382,30 @@ struct RenameConflictsVisitor<'a> { impl RenameConflictsVisitor<'_> { fn resolve_path(&mut self, node: ExprOrPatId, path: &Path) { - if let Path::BarePath(path) = path { - if let Some(name) = path.as_ident() { - if *name.symbol() == self.new_name { - if let Some(conflicting) = self.resolver.rename_will_conflict_with_renamed( - self.db, - name, - path, - self.body.expr_or_pat_path_hygiene(node), - self.to_be_renamed, - ) { - self.conflicts.insert(conflicting); - } - } else if *name.symbol() == self.old_name { - if let Some(conflicting) = - self.resolver.rename_will_conflict_with_another_variable( - self.db, - name, - path, - self.body.expr_or_pat_path_hygiene(node), - &self.new_name, - self.to_be_renamed, - ) - { - self.conflicts.insert(conflicting); - } + if let Path::BarePath(path) = path + && let Some(name) = path.as_ident() + { + if *name.symbol() == self.new_name { + if let Some(conflicting) = self.resolver.rename_will_conflict_with_renamed( + self.db, + name, + path, + self.body.expr_or_pat_path_hygiene(node), + self.to_be_renamed, + ) { + self.conflicts.insert(conflicting); } + } else if *name.symbol() == self.old_name + && let Some(conflicting) = self.resolver.rename_will_conflict_with_another_variable( + self.db, + name, + path, + self.body.expr_or_pat_path_hygiene(node), + &self.new_name, + self.to_be_renamed, + ) + { + self.conflicts.insert(conflicting); } } } |