diff --git a/src/tools/rust-analyzer/AI_POLICY.md b/src/tools/rust-analyzer/AI_POLICY.md index 2c1acbbff4cda..afe7f82a0a11c 100644 --- a/src/tools/rust-analyzer/AI_POLICY.md +++ b/src/tools/rust-analyzer/AI_POLICY.md @@ -1,6 +1,7 @@ We allow using AI (i.e., LLMs) as tools for contributing to rust-analyzer. However, you remain responsible for any code you publish and we are responsible for any code we merge and release. We hold a high bar for all contributions to our projects. +Also, we kindly ask you to disclose usage of AI tools in your contributions. **AI should not be used to generate comments when communicating with maintainers**. We expect comments on our projects to be written by humans. diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 8d9304dfaf316..b2dc4ceeddca6 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1455,7 +1455,7 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.9.0" +version = "0.10.0" dependencies = [ "anyhow", "crossbeam-channel", diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index a681ac72b701e..94d9d08e50ff4 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -315,6 +315,11 @@ impl Default for Nonce { } impl Nonce { + #[inline] + pub const fn invalid() -> Nonce { + Nonce(usize::MAX) + } + #[inline] pub fn new() -> Nonce { Nonce(NEXT_NONCE.fetch_add(1, std::sync::atomic::Ordering::SeqCst)) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs index 27f9763f088a4..d55509e2f0884 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -380,6 +380,29 @@ pub fn parse_extra_crate_attrs(db: &dyn SourceDatabase, krate: Crate) -> Option< Some(p.tree()) } +/// Whether the crate root declares `#![no_std]`, looking through `cfg_attr` gating. +#[salsa::tracked(returns(copy))] +pub(crate) fn crate_supports_no_std(db: &dyn SourceDatabase, krate: Crate) -> bool { + fn contains_no_std(meta: ast::Meta) -> bool { + match meta { + ast::Meta::PathMeta(meta) => meta.path().is1("no_std"), + ast::Meta::CfgAttrMeta(meta) => meta.metas().any(contains_no_std), + ast::Meta::UnsafeMeta(meta) => meta.meta().is_some_and(contains_no_std), + ast::Meta::CfgMeta(_) | ast::Meta::KeyValueMeta(_) | ast::Meta::TokenTreeMeta(_) => { + false + } + } + } + + let root_file = krate.root_file_id(db).parse(db).tree(); + parse_extra_crate_attrs(db, krate) + .into_iter() + .flat_map(|extra| extra.attrs()) + .chain(root_file.attrs()) + .filter_map(|attr| attr.meta()) + .any(contains_no_std) +} + fn attrs_source( db: &dyn SourceDatabase, owner: AttrDefId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 6ec78235feb5e..dec911f7161da 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -947,7 +947,7 @@ impl ExpressionStore { } } -pub trait StoreVisitor { +pub trait StoreVisitor: Sized { fn on_expr(&mut self, expr: ExprId) { let _ = expr; } @@ -963,6 +963,26 @@ pub trait StoreVisitor { fn on_lifetime(&mut self, lifetime: LifetimeRefId) { let _ = lifetime; } + + fn on_generic_args(&mut self, args: &GenericArgs) { + visit_generic_args(self, args); + } +} + +pub(crate) fn visit_generic_args(visitor: &mut V, args: &GenericArgs) { + let GenericArgs { args, bindings, parenthesized: _, has_self_type: _ } = args; + for arg in args { + match arg { + GenericArg::Type(arg) => visitor.on_type(*arg), + GenericArg::Const(ConstRef { expr }) => visitor.on_anon_const_expr(*expr), + GenericArg::Lifetime(arg) => visitor.on_lifetime(*arg), + } + } + for AssociatedTypeBinding { name: _, args, type_ref, bounds } in bindings { + visitor.on_generic_args_opt(args); + visitor.on_type_opt(*type_ref); + visitor.on_type_bounds(bounds); + } } impl StoreVisitor for &mut V { @@ -981,25 +1001,13 @@ impl StoreVisitor for &mut V { fn on_lifetime(&mut self, lifetime: LifetimeRefId) { V::on_lifetime(self, lifetime); } -} -trait StoreVisitorExt: StoreVisitor { fn on_generic_args(&mut self, args: &GenericArgs) { - let GenericArgs { args, bindings, parenthesized: _, has_self_type: _ } = args; - for arg in args { - match arg { - GenericArg::Type(arg) => self.on_type(*arg), - GenericArg::Const(ConstRef { expr }) => self.on_anon_const_expr(*expr), - GenericArg::Lifetime(arg) => self.on_lifetime(*arg), - } - } - for AssociatedTypeBinding { name: _, args, type_ref, bounds } in bindings { - self.on_generic_args_opt(args); - self.on_type_opt(*type_ref); - self.on_type_bounds(bounds); - } + V::on_generic_args(self, args); } +} +trait StoreVisitorExt: StoreVisitor { fn on_type_bound(&mut self, bound: &TypeBound) { match bound { TypeBound::Path(path_id, _) => self.on_type(path_id.type_ref()), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 0a1c23c2cc361..0320f6b93b973 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -35,13 +35,14 @@ use thin_vec::ThinVec; use tt::TextRange; use crate::{ - AdtId, BlockId, BlockLoc, ConstId, DefWithBodyId, FunctionId, GenericDefId, ImplId, - ItemContainerId, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, + AdtId, BlockId, BlockIdLt, ConstId, DefWithBodyId, FunctionId, GenericDefId, ImplId, + ItemContainerId, LoweringMode, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, + UnresolvedMacro, attrs::AttrFlags, expr_store::{ Body, BodySourceMap, ExprPtr, ExprRoot, ExpressionStore, ExpressionStoreBuilder, ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, LifetimePtr, - PatPtr, TypePtr, + PatPtr, StoreVisitor, TypePtr, body::Param, expander::Expander, lower::generics::ImplTraitLowerFn, @@ -57,7 +58,7 @@ use crate::{ item_tree::FieldsShape, lang_item::{LangItemTarget, LangItems}, nameres::{DefMap, LocalDefMap, MacroSubNs, block_def_map}, - signatures::StructSignature, + signatures::{StructSignature, TypeAliasSignature}, type_ref::{ ArrayType, ConstRef, FnType, LifetimeRef, LifetimeRefId, Mutability, PathId, Rawness, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId, UseArgRef, @@ -85,7 +86,7 @@ pub(super) fn lower_body( let mut self_param = None; let mut source_map_self_param = None; let mut params = vec![]; - let mut collector = ExprCollector::new(db, module, current_file_id); + let mut collector = ExprCollector::new(db, module, current_file_id, LoweringMode::Analysis); let skip_body = AttrFlags::query( db, @@ -203,7 +204,8 @@ pub(crate) fn lower_type_ref( module: ModuleId, type_ref: InFile>, ) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId) { - let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id); + let mut expr_collector = + ExprCollector::new(db, module, type_ref.file_id, LoweringMode::Analysis); let type_ref = expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator); let (store, source_map) = expr_collector.store.finish(); @@ -217,8 +219,9 @@ pub fn lower_generic_params( file_id: HirFileId, param_list: Option, where_clause: Option, + mode: LoweringMode, ) -> (ExpressionStore, GenericParams, ExpressionStoreSourceMap) { - let mut expr_collector = ExprCollector::new(db, module, file_id); + let mut expr_collector = ExprCollector::new(db, module, file_id, mode); let mut collector = generics::GenericParamsCollector::new(def); collector.lower(&mut expr_collector, param_list, where_clause); let params = collector.finish(); @@ -232,7 +235,8 @@ pub(crate) fn lower_impl( impl_syntax: InFile, impl_id: ImplId, ) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId, Option, GenericParams) { - let mut expr_collector = ExprCollector::new(db, module, impl_syntax.file_id); + let mut expr_collector = + ExprCollector::new(db, module, impl_syntax.file_id, LoweringMode::Analysis); let self_ty = expr_collector.lower_type_ref_opt_disallow_impl_trait(impl_syntax.value.self_ty()); let trait_ = impl_syntax.value.trait_().and_then(|it| match &it { @@ -260,7 +264,8 @@ pub(crate) fn lower_trait( trait_syntax: InFile, trait_id: TraitId, ) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams) { - let mut expr_collector = ExprCollector::new(db, module, trait_syntax.file_id); + let mut expr_collector = + ExprCollector::new(db, module, trait_syntax.file_id, LoweringMode::Analysis); let mut collector = generics::GenericParamsCollector::with_self_param( &mut expr_collector, trait_id.into(), @@ -283,7 +288,7 @@ pub(crate) fn lower_type_alias( type_alias_id: TypeAliasId, ) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams, Box<[TypeBound]>, Option) { - let mut expr_collector = ExprCollector::new(db, module, alias.file_id); + let mut expr_collector = ExprCollector::new(db, module, alias.file_id, LoweringMode::Analysis); let bounds = alias .value .type_bound_list() @@ -325,68 +330,74 @@ pub(crate) fn lower_function( bool, bool, ) { - let mut expr_collector = ExprCollector::new(db, module, fn_.file_id); + let mut expr_collector = ExprCollector::new(db, module, fn_.file_id, LoweringMode::Analysis); let mut collector = generics::GenericParamsCollector::new(function_id.into()); collector.lower(&mut expr_collector, fn_.value.generic_param_list(), fn_.value.where_clause()); let mut params = vec![]; let mut has_self_param = false; let mut has_variadic = false; collector.collect_impl_trait(&mut expr_collector, |collector, mut impl_trait_lower_fn| { - if let Some(param_list) = fn_.value.param_list() { - if let Some(param) = param_list.self_param() { - let enabled = collector.check_cfg(¶m); - if enabled { - has_self_param = true; - params.push(match param.ty() { - Some(ty) => collector.lower_type_ref(ty, &mut impl_trait_lower_fn), - None => { - let self_type = collector.alloc_type_ref_desugared(TypeRef::Path( - Name::new_symbol_root(sym::Self_).into(), - )); - let lifetime = param - .lifetime() - .map(|lifetime| collector.lower_lifetime_ref(lifetime)); - match param.kind() { - ast::SelfParamKind::Owned => self_type, - ast::SelfParamKind::Ref => collector.alloc_type_ref_desugared( - TypeRef::Reference(Box::new(RefType { - ty: self_type, - lifetime, - mutability: Mutability::Shared, - })), - ), - ast::SelfParamKind::MutRef => collector.alloc_type_ref_desugared( - TypeRef::Reference(Box::new(RefType { - ty: self_type, - lifetime, - mutability: Mutability::Mut, - })), - ), + collector.with_lifetime_bound_scope(LifetimeBoundScope::Argument, |collector| { + if let Some(param_list) = fn_.value.param_list() { + if let Some(param) = param_list.self_param() { + let enabled = collector.check_cfg(¶m); + if enabled { + has_self_param = true; + params.push(match param.ty() { + Some(ty) => collector.lower_type_ref(ty, &mut impl_trait_lower_fn), + None => { + let self_type = collector.alloc_type_ref_desugared(TypeRef::Path( + Name::new_symbol_root(sym::Self_).into(), + )); + let lifetime = param + .lifetime() + .map(|lifetime| collector.lower_lifetime_ref(lifetime)); + match param.kind() { + ast::SelfParamKind::Owned => self_type, + ast::SelfParamKind::Ref => collector.alloc_type_ref_desugared( + TypeRef::Reference(Box::new(RefType { + ty: self_type, + lifetime, + mutability: Mutability::Shared, + })), + ), + ast::SelfParamKind::MutRef => collector + .alloc_type_ref_desugared(TypeRef::Reference(Box::new( + RefType { + ty: self_type, + lifetime, + mutability: Mutability::Mut, + }, + ))), + } } - } - }); + }); + } + } + let p = param_list + .params() + .filter(|param| collector.check_cfg(param)) + .filter(|param| { + let is_variadic = param.dotdotdot_token().is_some(); + has_variadic |= is_variadic; + !is_variadic + }) + .map(|param| param.ty()) + // FIXME + .collect::>(); + for p in p { + params.push(collector.lower_type_ref_opt(p, &mut impl_trait_lower_fn)); } } - let p = param_list - .params() - .filter(|param| collector.check_cfg(param)) - .filter(|param| { - let is_variadic = param.dotdotdot_token().is_some(); - has_variadic |= is_variadic; - !is_variadic - }) - .map(|param| param.ty()) - // FIXME - .collect::>(); - for p in p { - params.push(collector.lower_type_ref_opt(p, &mut impl_trait_lower_fn)); - } - } + }) }); - let generics = collector.finish(); let return_type = fn_.value.ret_type().map(|ret_type| { - expr_collector.lower_type_ref_opt(ret_type.ty(), &mut ExprCollector::impl_trait_allocator) + expr_collector.with_lifetime_bound_scope(LifetimeBoundScope::Return, |this| { + this.lower_type_ref_opt(ret_type.ty(), &mut ExprCollector::impl_trait_allocator) + }) }); + collector.update_to_late_bound_lifetimes(&expr_collector.named_lifetime_store); + let generics = collector.finish(); let return_type = if fn_.value.async_token().is_some() || fn_.value.gen_token().is_some() { let (path, assoc_name) = @@ -443,8 +454,10 @@ pub struct ExprCollector<'db> { def_map: &'db DefMap, local_def_map: &'db LocalDefMap, module: ModuleId, + lowering_mode: LoweringMode, lang_items: OnceCell<&'db LangItems>, pub store: ExpressionStoreBuilder, + pub named_lifetime_store: NamedLifetimeStore, // state stuff // Prevent nested impl traits like `impl Foo`. @@ -551,11 +564,54 @@ impl BindingList { } } +#[derive(Debug, Default)] +pub struct NamedLifetimeStore { + lifetime_bound_scope: Option, + lifetimes_in_where_clause: FxIndexSet, + lifetimes_constrained_by_input: FxIndexSet, + lifetimes_in_output: FxIndexSet, +} + +#[derive(Debug)] +enum LifetimeBoundScope { + Argument, + Return, + WhereClause, + ImplTrait { is_argument_scope: bool }, +} + +impl NamedLifetimeStore { + /// Adds in the lifetime one of three lists fields if + /// `lifetime_bound_context` is `Some` and based on enum variant. + pub(crate) fn push_named_lifetime(&mut self, lifetime: Name) { + match self.lifetime_bound_scope { + Some(LifetimeBoundScope::Argument) => { + self.lifetimes_constrained_by_input.insert(lifetime); + } + Some(LifetimeBoundScope::Return) => { + self.lifetimes_in_output.insert(lifetime); + } + Some(LifetimeBoundScope::WhereClause) => { + self.lifetimes_in_where_clause.insert(lifetime); + } + Some(LifetimeBoundScope::ImplTrait { is_argument_scope }) => { + if is_argument_scope { + self.lifetimes_in_where_clause.insert(lifetime); + } else { + self.lifetimes_in_output.insert(lifetime); + } + } + None => (), + }; + } +} + impl<'db> ExprCollector<'db> { pub fn new( db: &dyn SourceDatabase, module: ModuleId, current_file_id: HirFileId, + lowering_mode: LoweringMode, ) -> ExprCollector<'_> { let (def_map, local_def_map) = module.local_def_map(db); let expander = Expander::new(db, current_file_id, def_map); @@ -564,6 +620,7 @@ impl<'db> ExprCollector<'db> { db, cfg_options: krate.cfg_options(db), module, + lowering_mode, def_map, local_def_map, lang_items: OnceCell::new(), @@ -578,6 +635,7 @@ impl<'db> ExprCollector<'db> { outer_impl_trait: false, krate, name_generator_index: 0, + named_lifetime_store: NamedLifetimeStore::default(), }; result.store.inference_roots = Some(SmallVec::new()); result @@ -717,8 +775,16 @@ impl<'db> ExprCollector<'db> { TypeRef::Error } else { return self.with_outer_impl_trait_scope(true, |this| { - let type_bounds = - this.type_bounds_from_ast(inner.type_bound_list(), impl_trait_lower_fn); + let is_argument_scope = this.is_argument_lt_bound_scope(); + let type_bounds = this.with_lifetime_bound_scope( + LifetimeBoundScope::ImplTrait { is_argument_scope }, + |this| { + this.type_bounds_from_ast( + inner.type_bound_list(), + impl_trait_lower_fn, + ) + }, + ); impl_trait_lower_fn(this, AstPtr::new(&node), type_bounds) }); } @@ -781,6 +847,10 @@ impl<'db> ExprCollector<'db> { lifetime_ref: LifetimeRef, node: LifetimePtr, ) -> LifetimeRefId { + if let LifetimeRef::Named(name) = &lifetime_ref { + self.named_lifetime_store.push_named_lifetime(name.clone()); + } + let id = self.store.lifetimes.alloc(lifetime_ref); let ptr = self.expander.in_file(node); self.store.lifetime_map_back.insert(id, ptr); @@ -1294,16 +1364,18 @@ impl<'db> ExprCollector<'db> { let capture_by = if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; self.with_label_rib(RibKind::Closure, |this| { - this.with_awaitable_block(Awaitable::Yes, |this| { - this.collect_block_(e, |this, id, statements, tail| { - this.desugared_coroutine_expr( - CoroutineKind::Async, - CoroutineSource::Block, - capture_by, - id, - statements, - tail, - ) + this.with_binding_owner(|this| { + this.with_awaitable_block(Awaitable::Yes, |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::Async, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) }) }) }) @@ -1312,16 +1384,18 @@ impl<'db> ExprCollector<'db> { let capture_by = if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; self.with_label_rib(RibKind::Closure, |this| { - this.with_awaitable_block(Awaitable::No("non-async gen block"), |this| { - this.collect_block_(e, |this, id, statements, tail| { - this.desugared_coroutine_expr( - CoroutineKind::Gen, - CoroutineSource::Block, - capture_by, - id, - statements, - tail, - ) + this.with_binding_owner(|this| { + this.with_awaitable_block(Awaitable::No("non-async gen block"), |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::Gen, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) }) }) }) @@ -1330,16 +1404,18 @@ impl<'db> ExprCollector<'db> { let capture_by = if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; self.with_label_rib(RibKind::Closure, |this| { - this.with_awaitable_block(Awaitable::Yes, |this| { - this.collect_block_(e, |this, id, statements, tail| { - this.desugared_coroutine_expr( - CoroutineKind::AsyncGen, - CoroutineSource::Block, - capture_by, - id, - statements, - tail, - ) + this.with_binding_owner(|this| { + this.with_awaitable_block(Awaitable::Yes, |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::AsyncGen, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) }) }) }) @@ -1857,12 +1933,20 @@ impl<'db> ExprCollector<'db> { } let mut elements = e.exprs(); + let mut rest_ptr = None; let prefix = elements .by_ref() - .map_while(|elem| collect_possibly_rest(self, elem).left()) + .map_while(|elem| match collect_possibly_rest(self, elem.clone()) { + Either::Left(elem) => Some(elem), + Either::Right(()) => { + rest_ptr = Some(AstPtr::new(&elem)); + None + } + }) .collect(); let suffix = elements.map(|elem| self.collect_expr_as_pat(elem)).collect(); - self.alloc_pat_from_expr(Pat::Slice { prefix, slice: None, suffix }, syntax_ptr) + let slice = rest_ptr.map(|ptr| self.alloc_pat_from_expr(Pat::Rest, ptr)); + self.alloc_pat_from_expr(Pat::Slice { prefix, slice, suffix }, syntax_ptr) } ast::Expr::CallExpr(e) => { let path = collect_path(self, e.expr()?)?; @@ -2533,10 +2617,12 @@ impl<'db> ExprCollector<'db> { block: ast::BlockExpr, mk_block: impl FnOnce(&mut Self, Option, Box<[Statement]>, Option) -> Expr, ) -> ExprId { - let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| { + let block_id = (|| { + let token = self.lowering_mode.allow_tracked_structs()?; + let file_local_id = self.expander.ast_id_map().ast_id_for_block(&block)?; let ast_id = self.expander.in_file(file_local_id); - BlockId::new(self.db, BlockLoc { ast_id, module: self.module }) - }); + Some(unsafe { BlockIdLt::new(self.db, ast_id, self.module, token).to_static() }) + })(); let (module, def_map) = match block_id.map(|block_id| (block_def_map(self.db, block_id), block_id)) { @@ -3359,6 +3445,137 @@ impl ExprCollector<'_> { fn hygiene_id_for(&self, range: TextRange) -> HygieneId { self.expander.hygiene_for_range(self.db, range) } + + fn with_lifetime_bound_scope( + &mut self, + bound_scope: LifetimeBoundScope, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + let old = self.named_lifetime_store.lifetime_bound_scope.replace(bound_scope); + let res = f(self); + self.named_lifetime_store.lifetime_bound_scope = old; + res + } + + fn for_path_type_projection(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { + if self.is_argument_lt_bound_scope() { + let old = self.named_lifetime_store.lifetime_bound_scope.take(); + let res = f(self); + self.named_lifetime_store.lifetime_bound_scope = old; + res + } else { + f(self) + } + } + + fn push_named_target_lifetime(&mut self, id: LifetimeRefId) { + if let LifetimeRef::Named(name) = &self.store.lifetimes[id] { + self.named_lifetime_store.push_named_lifetime(name.clone()); + } + } + + fn extend_type_alias_lifetime(&mut self, lifetimes: impl Iterator) { + self.named_lifetime_store.lifetimes_constrained_by_input.extend(lifetimes); + } + + fn is_argument_lt_bound_scope(&mut self) -> bool { + matches!(self.named_lifetime_store.lifetime_bound_scope, Some(LifetimeBoundScope::Argument)) + } + + fn get_constrained_lifetimes_if_type_alias( + &mut self, + mod_path: &intern::Interned, + generic_args: Option<&GenericArgs>, + ) -> Option> { + let r_path = self.def_map.resolve_path( + self.local_def_map, + self.db, + self.module, + mod_path, + BuiltinShadowMode::Module, + None, + ); + let def_id = r_path.0.types.map(|item| item.def)?; + let res = if let crate::ModuleDefId::TypeAliasId(id) = def_id { + let Some(generic_args) = generic_args else { return Some(FxIndexSet::default()) }; + + let constrained_lt_indices = get_constrained_lifetimes(self.db, id); + let res = constrained_lt_indices + .iter() + .filter_map(|&idx| { + let lt_ref = generic_args + .args + .iter() + .filter_map(|arg| match arg { + &GenericArg::Lifetime(lt_ref) => Some(lt_ref), + GenericArg::Type(_) | GenericArg::Const(_) => None, + }) + .nth(idx as usize)?; + match &self.store.lifetimes[lt_ref] { + LifetimeRef::Named(name) => Some(name.clone()), + _ => None, + } + }) + .collect(); + Some(res) + } else { + None + }; + return res; + + #[salsa::tracked(returns(deref), cycle_result = get_constrained_lifetimes_cycle_result)] + fn get_constrained_lifetimes( + db: &dyn SourceDatabase, + type_alias_id: TypeAliasId, + ) -> Box<[u32]> { + let TypeAliasSignature { generic_params, store, ty, .. } = + TypeAliasSignature::of(db, type_alias_id); + let &Some(ty) = ty else { return Default::default() }; + + let mut visitor = Visitor { + store, + generic_params, + parent: type_alias_id, + constrained_lt_indices: Vec::new(), + }; + store.visit_type_ref_children(ty, &mut visitor); + + return visitor.constrained_lt_indices.into_boxed_slice(); + + struct Visitor<'a> { + store: &'a ExpressionStore, + generic_params: &'a GenericParams, + parent: TypeAliasId, + constrained_lt_indices: Vec, + } + + impl StoreVisitor for Visitor<'_> { + fn on_lifetime(&mut self, lifetime: LifetimeRefId) { + if let LifetimeRef::Named(lifetime_name) = &self.store[lifetime] + && let Some(param_id) = self + .generic_params + .find_lifetime_by_name(lifetime_name, self.parent.into()) + { + self.constrained_lt_indices.push(param_id.local_id.into_raw().into_u32()); + } + } + + fn on_generic_args(&mut self, args: &GenericArgs) { + if !args.has_self_type { + crate::expr_store::visit_generic_args(self, args); + } + } + } + } + + fn get_constrained_lifetimes_cycle_result( + _db: &dyn SourceDatabase, + _: salsa::Id, + _id: TypeAliasId, + ) -> Box<[u32]> { + Default::default() + } + } } fn comma_follows_token(t: Option) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs index 7ef9c80de1ae8..2119f19c065d2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs @@ -12,10 +12,13 @@ use thin_vec::ThinVec; use crate::{ GenericDefId, TypeOrConstParamId, TypeParamId, - expr_store::{TypePtr, lower::ExprCollector}, + expr_store::{ + TypePtr, + lower::{ExprCollector, LifetimeBoundScope, NamedLifetimeStore}, + }, hir::generics::{ - ConstParamData, GenericParams, LifetimeParamData, TypeOrConstParamData, TypeParamData, - TypeParamProvenance, WherePredicate, + ConstParamData, GenericParams, LifetimeBoundType, LifetimeParamData, TypeOrConstParamData, + TypeParamData, TypeParamProvenance, WherePredicate, }, type_ref::{LifetimeRef, LifetimeRefId, TypeBound, TypeRef, TypeRefId}, }; @@ -62,7 +65,7 @@ impl GenericParamsCollector { self.lower_param_list(ec, params) } if let Some(where_clause) = where_clause { - self.lower_where_predicates(ec, where_clause); + self.lower_where_predicates(ec, where_clause) } } @@ -118,7 +121,9 @@ impl GenericParamsCollector { local_id: idx, })); let type_ref = ec.alloc_type_ref_desugared(type_ref); - self.lower_bounds(ec, type_param.type_bound_list(), Either::Left(type_ref)); + ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| { + self.lower_bounds(ec, type_param.type_bound_list(), Either::Left(type_ref)) + }); } ast::GenericParam::ConstParam(const_param) => { let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); @@ -133,13 +138,18 @@ impl GenericParamsCollector { ast::GenericParam::LifetimeParam(lifetime_param) => { let lifetime = ec.lower_lifetime_ref_opt(lifetime_param.lifetime()); if let LifetimeRef::Named(name) = &ec.store.lifetimes[lifetime] { - let param = LifetimeParamData { name: name.clone() }; + let param = LifetimeParamData { + name: name.clone(), + bound_type: LifetimeBoundType::EarlyBound, + }; let _idx = self.lifetimes.alloc(param); - self.lower_bounds( - ec, - lifetime_param.type_bound_list(), - Either::Right(lifetime), - ); + ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| { + self.lower_bounds( + ec, + lifetime_param.type_bound_list(), + Either::Right(lifetime), + ) + }); } } } @@ -151,33 +161,35 @@ impl GenericParamsCollector { ec: &mut ExprCollector<'_>, where_clause: ast::WhereClause, ) { - for pred in where_clause.predicates() { - let target = if let Some(type_ref) = pred.ty() { - Either::Left( - ec.lower_type_ref(type_ref, &mut ExprCollector::impl_trait_error_allocator), - ) - } else if let Some(lifetime) = pred.lifetime() { - Either::Right(ec.lower_lifetime_ref(lifetime)) - } else { - continue; - }; + ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| { + for pred in where_clause.predicates() { + let target = if let Some(type_ref) = pred.ty() { + Either::Left( + ec.lower_type_ref(type_ref, &mut ExprCollector::impl_trait_error_allocator), + ) + } else if let Some(lifetime) = pred.lifetime() { + Either::Right(ec.lower_lifetime_ref(lifetime)) + } else { + continue; + }; - let lifetimes: Option> = - pred.for_binder().and_then(|it| it.generic_param_list()).map(|param_list| { - // Higher-Ranked Trait Bounds - param_list - .lifetime_params() - .map(|lifetime_param| { - lifetime_param - .lifetime() - .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())) - }) - .collect() - }); - for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { - self.lower_type_bound_as_predicate(ec, bound, lifetimes.as_deref(), target); + let lifetimes: Option> = + pred.for_binder().and_then(|it| it.generic_param_list()).map(|param_list| { + // Higher-Ranked Trait Bounds + param_list + .lifetime_params() + .map(|lifetime_param| { + lifetime_param + .lifetime() + .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())) + }) + .collect() + }); + for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { + self.lower_type_bound_as_predicate(ec, bound, lifetimes.as_deref(), target); + } } - } + }); } fn lower_bounds( @@ -221,6 +233,9 @@ impl GenericParamsCollector { } (Either::Right(_), TypeBound::ForLifetime(..) | TypeBound::Path(..)) => return, }; + if let WherePredicate::Lifetime { target, .. } = predicate { + ec.push_named_target_lifetime(target); + } self.where_predicates.push(predicate); } @@ -269,4 +284,24 @@ impl GenericParamsCollector { self.lower_bounds(ec, Some(bounds), Either::Left(self_)); } } + + pub(crate) fn update_to_late_bound_lifetimes( + &mut self, + named_lifetime_store: &NamedLifetimeStore, + ) { + for (_param_id, lifetime) in self.lifetimes.iter_mut() { + let lifetime_name = &lifetime.name; + if named_lifetime_store.lifetimes_in_where_clause.contains(lifetime_name) { + continue; + } + + if !named_lifetime_store.lifetimes_constrained_by_input.contains(lifetime_name) + && named_lifetime_store.lifetimes_in_output.contains(lifetime_name) + { + continue; + } + + lifetime.bound_type = LifetimeBoundType::LateBound + } + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs index 236255c404f89..5d45a4fe836fa 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs @@ -54,6 +54,13 @@ pub(super) fn lower_path( ast_segments.push(_segment.clone()); segments.push(name); }; + + let old_lifetimes_constrained_by_input = if collector.is_argument_lt_bound_scope() { + Some(std::mem::take(&mut collector.named_lifetime_store.lifetimes_constrained_by_input)) + } else { + None + }; + loop { let Some(segment) = path.segment() else { segments.push(Name::missing()); @@ -112,7 +119,10 @@ pub(super) fn lower_path( ast::PathSegmentKind::Type { type_ref, trait_ref } => { debug_assert!(path.qualifier().is_none()); // this can only occur at the first segment - let self_type = collector.lower_type_ref(type_ref?, impl_trait_lower_fn); + let type_ref = type_ref?; + let self_type = collector.for_path_type_projection(|collector| { + collector.lower_type_ref(type_ref, impl_trait_lower_fn) + }); match trait_ref { // ::foo @@ -122,7 +132,9 @@ pub(super) fn lower_path( } // >::Foo desugars to Trait::Foo Some(trait_ref) => { - let path = collector.lower_path(trait_ref.path()?, impl_trait_lower_fn)?; + let path = collector.for_path_type_projection(|collector| { + collector.lower_path(trait_ref.path()?, impl_trait_lower_fn) + })?; // FIXME: Unnecessary clone collector.alloc_type_ref( TypeRef::Path(path.clone()), @@ -242,6 +254,24 @@ pub(super) fn lower_path( } let mod_path = Interned::new(ModPath::from_segments(kind, segments)); + + let type_alias_constrained_lifetimes = collector.get_constrained_lifetimes_if_type_alias( + &mod_path, + generic_args.last().and_then(|g| g.as_ref()), + ); + if let Some(old_lifetimes_constrained_by_input) = old_lifetimes_constrained_by_input { + if let Some(lifetimes) = type_alias_constrained_lifetimes { + collector.named_lifetime_store.lifetimes_constrained_by_input = + old_lifetimes_constrained_by_input; + collector.extend_type_alias_lifetime(lifetimes.into_iter()); + } else { + collector + .named_lifetime_store + .lifetimes_constrained_by_input + .extend(old_lifetimes_constrained_by_input); + } + } + if type_anchor.is_none() && generic_args.is_empty() { return Some(Path::BarePath(mod_path)); } else { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs index f507841a91bf5..b1d0ebb97d3a6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs @@ -20,8 +20,12 @@ use crate::{ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option) { let (db, file_id) = TestDB::with_single_file(""); let krate = db.fetch_test_crate(); - let mut ctx = - ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into()); + let mut ctx = ExprCollector::new( + &db, + crate_def_map(&db, krate).root_module_id(), + file_id.into(), + crate::LoweringMode::Analysis, + ); let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator); let (store, _) = ctx.store.finish(); (db, store, lowered_path) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs index f02e0f349bc93..a39a00b7a7df7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs @@ -196,7 +196,7 @@ fn f() { ), block: Some( BlockId( - 3c01, + Id(3c01), ), ), containing_module_inside_def_map: None, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs index 24bdc6ece31e7..460ab6418d92d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs @@ -199,6 +199,19 @@ fn allowed3(baz: impl Baz>) {} ); } +#[test] +fn type_alias_constrained_lifetime_with_elided_lifetime_args() { + lower_and_print( + r#" +type Alias<'a, 'b, T> = &'b T; +fn f(_: Alias) {} +"#, + expect![[r#" + fn f(Alias::) {...} + "#]], + ); +} + #[test] fn regression_21138() { lower_and_print( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 82ddaff189975..2f502e49157c2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -12,7 +12,8 @@ use intern::sym; use rustc_hash::FxHashSet; use crate::{ - ModuleDefId, ModuleId, + ModuleDefId, ModuleIdLt, + attrs::crate_supports_no_std, import_map::ImportMap, item_scope::ItemInNs, nameres::DefMap, @@ -38,7 +39,7 @@ pub struct FindPathConfig { pub fn find_path( db: &dyn SourceDatabase, item: ItemInNs, - from: ModuleId, + from: ModuleIdLt<'_>, mut prefix_kind: PrefixKind, ignore_local_imports: bool, mut cfg: FindPathConfig, @@ -59,7 +60,9 @@ pub fn find_path( let from_def_map = from.def_map(db); - cfg.prefer_no_std = cfg.prefer_no_std || from_def_map.is_no_std(); + let from_crate = from.krate(db); + cfg.prefer_no_std = + cfg.prefer_no_std || from_def_map.is_no_std() || crate_supports_no_std(db, from_crate); find_path_inner( &FindPathCtx { @@ -69,7 +72,7 @@ pub fn find_path( ignore_local_imports, is_std_item: item_module.krate(db).data(db).origin.is_lang(), from, - from_crate: from.krate(db), + from_crate, crate_root: from_def_map.crate_root(db), from_def_map, fuel: Cell::new(FIND_PATH_FUEL), @@ -118,14 +121,14 @@ struct FindPathCtx<'db> { cfg: FindPathConfig, ignore_local_imports: bool, is_std_item: bool, - from: ModuleId, + from: ModuleIdLt<'db>, from_crate: Crate, - crate_root: ModuleId, + crate_root: ModuleIdLt<'db>, from_def_map: &'db DefMap, fuel: Cell, } -/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId +/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleIdLt<'_> fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option { // - if the item is a module, jump straight to module search if !ctx.is_std_item @@ -171,10 +174,10 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt } #[tracing::instrument(skip_all)] -fn find_path_for_module( - ctx: &FindPathCtx<'_>, - visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, - module_id: ModuleId, +fn find_path_for_module<'db>( + ctx: &'db FindPathCtx<'db>, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>, + module_id: ModuleIdLt<'db>, maybe_extern: bool, max_len: usize, ) -> Option { @@ -231,7 +234,7 @@ fn find_path_for_module( ctx.db, ctx.from_def_map, ctx.from, - ItemInNs::Types(module_id.into()), + ItemInNs::Types(unsafe { module_id.to_static() }.into()), ctx.ignore_local_imports, ); if let Some(scope_name) = scope_name { @@ -253,7 +256,7 @@ fn find_path_for_module( } // - if the module is in the prelude, return it by that path - let item = ItemInNs::Types(module_id.into()); + let item = ItemInNs::Types(unsafe { module_id.to_static() }.into()); if let Some(choice) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) { return Some(choice); } @@ -266,10 +269,10 @@ fn find_path_for_module( best_choice } -fn find_in_scope( - db: &dyn SourceDatabase, +fn find_in_scope<'db>( + db: &'db dyn SourceDatabase, def_map: &DefMap, - from: ModuleId, + from: ModuleIdLt<'db>, item: ItemInNs, ignore_local_imports: bool, ) -> Option { @@ -287,7 +290,7 @@ fn find_in_prelude( db: &dyn SourceDatabase, local_def_map: &DefMap, item: ItemInNs, - from: ModuleId, + from: ModuleIdLt<'_>, ) -> Option { let (prelude_module, _) = local_def_map.prelude()?; let prelude_def_map = prelude_module.def_map(db); @@ -319,8 +322,8 @@ fn find_in_prelude( fn is_kw_kind_relative_to_from( db: &dyn SourceDatabase, def_map: &DefMap, - item: ModuleId, - from: ModuleId, + item: ModuleIdLt<'_>, + from: ModuleIdLt<'_>, ) -> Option { if item.krate(db) != from.krate(db) || item.block(db).is_some() || from.block(db).is_some() { return None; @@ -341,9 +344,9 @@ fn is_kw_kind_relative_to_from( } #[tracing::instrument(skip_all)] -fn calculate_best_path( - ctx: &FindPathCtx<'_>, - visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, +fn calculate_best_path<'db>( + ctx: &'db FindPathCtx<'db>, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>, item: ItemInNs, max_len: usize, best_choice: &mut Option, @@ -381,9 +384,9 @@ fn calculate_best_path( } } -fn find_in_sysroot( - ctx: &FindPathCtx<'_>, - visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, +fn find_in_sysroot<'db>( + ctx: &'db FindPathCtx<'db>, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>, item: ItemInNs, max_len: usize, best_choice: &mut Option, @@ -427,9 +430,9 @@ fn find_in_sysroot( }); } -fn find_in_dep( - ctx: &FindPathCtx<'_>, - visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, +fn find_in_dep<'db>( + ctx: &'db FindPathCtx<'db>, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>, item: ItemInNs, max_len: usize, best_choice: &mut Option, @@ -464,9 +467,9 @@ fn find_in_dep( } } -fn calculate_best_path_local( - ctx: &FindPathCtx<'_>, - visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, +fn calculate_best_path_local<'db>( + ctx: &'db FindPathCtx<'db>, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>, item: ItemInNs, max_len: usize, best_choice: &mut Option, @@ -563,11 +566,11 @@ fn path_kind_len(kind: PathKind) -> usize { } /// Finds locations in `from.krate` from which `item` can be imported by `from`. -fn find_local_import_locations( - ctx: &FindPathCtx<'_>, +fn find_local_import_locations<'db>( + ctx: &'db FindPathCtx<'db>, item: ItemInNs, - visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, - mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleId)>, &Name, ModuleId), + visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>, + mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>, &Name, ModuleIdLt<'db>), ) { let _p = tracing::info_span!("find_local_import_locations").entered(); let db = ctx.db; @@ -1485,7 +1488,7 @@ pub mod fmt { "#]], ); - // Should also work (on a best-effort basis) if `no_std` is conditional. + // Should also work (on a best-effort basis) if `no_std` is conditionally enabled. check_found_path( r#" //- /main.rs crate:main deps:core,std @@ -1501,6 +1504,37 @@ pub mod fmt { //- /zzz.rs crate:core +pub mod fmt { + pub struct Error; +} + "#, + "core::fmt::Error", + expect![[r#" + Plain (imports ✔): core::fmt::Error + Plain (imports ✖): core::fmt::Error + ByCrate(imports ✔): core::fmt::Error + ByCrate(imports ✖): core::fmt::Error + BySelf (imports ✔): core::fmt::Error + BySelf (imports ✖): core::fmt::Error + "#]], + ); + + // Should also work (on a best-effort basis) if `no_std` is conditionally disabled. + check_found_path( + r#" +//- /main.rs crate:main deps:core,std cfg:test +#![cfg_attr(not(test), no_std)] + +$0 + +//- /std.rs crate:std deps:core + +pub mod fmt { + pub use core::fmt::Error; +} + +//- /zzz.rs crate:core + pub mod fmt { pub struct Error; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index 36ae821d748e3..b6e9fc28207ac 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -33,6 +33,19 @@ pub struct TypeParamData { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct LifetimeParamData { pub name: Name, + pub bound_type: LifetimeBoundType, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum LifetimeBoundType { + EarlyBound, + LateBound, +} + +impl LifetimeParamData { + pub fn is_late_bound(&self) -> bool { + self.bound_type == LifetimeBoundType::LateBound + } } /// Data about a generic const parameter (to a function, struct, impl, ...). @@ -293,7 +306,12 @@ impl GenericParams { #[inline] pub fn len_lifetimes(&self) -> usize { - self.lifetimes.len() + self.lifetimes.len() - self.len_late_bound_lifetimes() + } + + #[inline] + pub fn len_late_bound_lifetimes(&self) -> usize { + self.lifetimes.iter().filter(|(_, p)| p.bound_type == LifetimeBoundType::LateBound).count() } #[inline] @@ -332,6 +350,20 @@ impl GenericParams { self.lifetimes.iter() } + #[inline] + pub fn iter_early_bound_lt( + &self, + ) -> impl DoubleEndedIterator { + self.lifetimes.iter().filter(|(_, p)| p.bound_type == LifetimeBoundType::EarlyBound) + } + + #[inline] + pub fn iter_late_bound_lt( + &self, + ) -> impl DoubleEndedIterator { + self.lifetimes.iter().filter(|(_, p)| p.bound_type == LifetimeBoundType::LateBound) + } + pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option { self.type_or_consts.iter().find_map(|(id, p)| { if p.name().as_ref() == Some(&name) && p.type_param().is_some() { @@ -376,4 +408,22 @@ impl GenericParams { if &p.name == name { Some(LifetimeParamId { local_id: id, parent }) } else { None } }) } + + pub fn lifetime_param_idx( + &self, + lifetime_param_id: &LocalLifetimeParamId, + ) -> Option<(usize, bool)> { + let mut late_bound_idx = 0; + self.iter_lt().enumerate().find_map(|(idx, (param_id, param_data))| { + let idx = if param_data.is_late_bound() { + let prev = late_bound_idx; + late_bound_idx += 1; + prev + } else { + idx - late_bound_idx + }; + + (param_id == *lifetime_param_id).then(|| (idx, param_data.is_late_bound())) + }) + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 16283a2cb372b..ac1d2fd2a4660 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -483,8 +483,11 @@ impl ItemScope { } pub(crate) fn remove_from_value_ns(&mut self, name: &Name, def: ModuleDefId) { - let entry = self.values.shift_remove(name); - assert!(entry.is_some_and(|entry| entry.def == def)) + // predicate needed since a different item with the same name may be registered instead, + // leading to `shift_remove` removing the wrong item. + if self.values.get(name).is_some_and(|entry| entry.def == def) { + let _ = self.values.shift_remove(name); + } } pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<&[MacroId]> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 8be44b0828866..cb6941aaa4cd4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -63,7 +63,7 @@ use syntax::{SourceFile, SyntaxKind, ast, match_ast}; use thin_vec::ThinVec; use tt::TextRange; -use crate::{BlockId, Lookup, attrs::parse_extra_crate_attrs}; +use crate::{BlockId, attrs::parse_extra_crate_attrs}; pub(crate) use crate::item_tree::{ attrs::*, @@ -204,10 +204,10 @@ pub(crate) fn block_item_tree_query( krate: Crate, ) -> ItemTree { let _p = tracing::info_span!("block_item_tree_query", ?block).entered(); - let loc = block.lookup(db); - let block = loc.ast_id.to_node(db); + let ast_id = block.ast_id(db); + let block = ast_id.to_node(db); - let ctx = lower::Ctx::new(db, loc.ast_id.file_id, krate); + let ctx = lower::Ctx::new(db, ast_id.file_id, krate); let mut item_tree = ctx.lower_block(&block); item_tree.shrink_to_fit(); item_tree diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index fa7cb525bbcd8..8b93fe5e2f01b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -50,7 +50,10 @@ mod macro_expansion_tests; #[cfg(test)] mod test_db; -use std::hash::{Hash, Hasher}; +use std::{ + fmt, + hash::{Hash, Hasher}, +}; use base_db::{Crate, SourceDatabase, impl_intern_key}; use hir_expand::{ @@ -462,13 +465,74 @@ pub struct ProcMacroLoc { impl_intern!(ProcMacroId, ProcMacroLoc); impl_loc!(ProcMacroLoc, id: Fn, container: ModuleId); -#[derive(Debug, Hash, PartialEq, Eq, Clone)] -pub struct BlockLoc { +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] +pub enum LoweringMode { + Analysis, + Ide, +} + +pub use self::tracked_struct_token::TrackedStructToken; +mod tracked_struct_token { + use super::LoweringMode; + + /// A token that is required to construct tracked structs. + /// This exists to prevent one from accidentally creating a tracked struct outside of a query which may happen for some codepaths. + pub struct TrackedStructToken { + // #[non_exhaustive] doesn't work for us here, we want it module focused. + _private: (), + } + + impl LoweringMode { + pub fn allow_tracked_structs(self) -> Option { + match self { + LoweringMode::Analysis => Some(TrackedStructToken { _private: () }), + LoweringMode::Ide => None, + } + } + } +} + +#[salsa_macros::tracked(constructor = new_)] +#[derive(PartialOrd, Ord)] +pub struct BlockIdLt<'db> { pub ast_id: AstId, /// The containing module. - pub module: ModuleId, + pub module: ModuleIdLt<'db>, +} +pub type BlockId = BlockIdLt<'static>; + +impl<'db> fmt::Debug for BlockIdLt<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("BlockId").field(&self.0).finish() + } +} + +impl<'db> BlockIdLt<'db> { + pub fn new( + db: &'db dyn SourceDatabase, + ast_id: AstId, + module: ModuleIdLt<'db>, + token: TrackedStructToken, + ) -> Self { + _ = token; + BlockIdLt::new_(db, ast_id, module) + } + + /// # Safety + /// + /// The caller must ensure that the `ModuleId` is not leaked outside of query computations. + pub unsafe fn to_static(self) -> BlockId { + unsafe { std::mem::transmute(self) } + } +} +impl BlockId { + /// # Safety + /// + /// The caller must ensure that the `BlockId` comes from the given database. + pub unsafe fn to_db<'db>(self, _db: &'db dyn SourceDatabase) -> BlockIdLt<'db> { + unsafe { std::mem::transmute(self) } + } } -impl_intern!(BlockId, BlockLoc); #[salsa_macros::tracked(debug)] #[derive(PartialOrd, Ord)] @@ -478,7 +542,7 @@ pub struct ModuleIdLt<'db> { /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the /// `BlockId` of that block expression. If `None`, this module is part of the crate-level /// `DefMap` of `krate`. - pub block: Option, + pub block: Option>, /// The parent module of this module, or `None` if this is the root module inside the def /// map (including for block def maps). pub containing_module_inside_def_map: Option>, @@ -487,30 +551,25 @@ pub struct ModuleIdLt<'db> { } pub type ModuleId = ModuleIdLt<'static>; -impl ModuleIdLt<'_> { +impl<'db> ModuleIdLt<'db> { /// # Safety /// /// The caller must ensure that the `ModuleId` is not leaked outside of query computations. pub unsafe fn to_static(self) -> ModuleId { unsafe { std::mem::transmute(self) } } -} -impl ModuleId { - /// # Safety - /// - /// The caller must ensure that the `ModuleId` comes from the given database. - pub unsafe fn to_db<'db>(self, _db: &'db dyn SourceDatabase) -> ModuleIdLt<'db> { - unsafe { std::mem::transmute(self) } - } - pub fn def_map(self, db: &dyn SourceDatabase) -> &DefMap { + pub fn def_map(self, db: &'db dyn SourceDatabase) -> &'db DefMap { match self.block(db) { Some(block) => block_def_map(db, block), None => crate_def_map(db, self.krate(db)), } } - pub(crate) fn local_def_map(self, db: &dyn SourceDatabase) -> (&DefMap, &LocalDefMap) { + pub(crate) fn local_def_map( + self, + db: &'db dyn SourceDatabase, + ) -> (&'db DefMap, &'db LocalDefMap) { match self.block(db) { Some(block) => (block_def_map(db, block), self.only_local_def_map(db)), None => { @@ -520,11 +579,11 @@ impl ModuleId { } } - pub(crate) fn only_local_def_map(self, db: &dyn SourceDatabase) -> &LocalDefMap { + pub(crate) fn only_local_def_map(self, db: &'db dyn SourceDatabase) -> &'db LocalDefMap { crate_local_def_map(db, self.krate(db)).local(db) } - pub fn crate_def_map(self, db: &dyn SourceDatabase) -> &DefMap { + pub fn crate_def_map(self, db: &'db dyn SourceDatabase) -> &'db DefMap { crate_def_map(db, self.krate(db)) } @@ -535,13 +594,9 @@ impl ModuleId { /// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing /// the block, if `self` corresponds to a block expression. - pub fn containing_module(self, db: &dyn SourceDatabase) -> Option { + pub fn containing_module(self, db: &'db dyn SourceDatabase) -> Option> { self.containing_module_inside_def_map(db) - .or_else(|| self.block(db).map(|block| block.loc(db).module)) - .map(|module| { - // SAFETY: Not sure. - unsafe { module.to_static() } - }) + .or_else(|| self.block(db).map(|block| block.module(db))) } pub fn is_block_module(self, db: &dyn SourceDatabase) -> bool { @@ -549,6 +604,15 @@ impl ModuleId { } } +impl ModuleId { + /// # Safety + /// + /// The caller must ensure that the `ModuleId` comes from the given database. + pub unsafe fn to_db<'db>(self, _db: &'db dyn SourceDatabase) -> ModuleIdLt<'db> { + unsafe { std::mem::transmute(self) } + } +} + impl HasModule for ModuleId { #[inline] fn module(&self, _db: &dyn SourceDatabase) -> ModuleId { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 00d069867203f..4b7856e0c1494 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -76,7 +76,7 @@ use triomphe::Arc; use tt::TextRange; use crate::{ - AstId, BlockId, BlockLoc, BuiltinDeriveImplId, ExternCrateId, FunctionId, FxIndexMap, Lookup, + AstId, BlockId, BlockIdLt, BuiltinDeriveImplId, ExternCrateId, FunctionId, FxIndexMap, Lookup, MacroCallStyles, MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId, item_scope::{BuiltinShadowMode, ItemScope}, item_tree::TreeId, @@ -272,12 +272,12 @@ struct BlockInfo { parent: ModuleId, } -impl std::ops::Index for DefMap { +impl std::ops::Index> for DefMap { type Output = ModuleData; - fn index(&self, id: ModuleId) -> &ModuleData { + fn index(&self, id: ModuleIdLt<'_>) -> &ModuleData { self.modules - .get(&id) + .get(&unsafe { id.to_static() }) .unwrap_or_else(|| panic!("ModuleId not found in ModulesMap {:#?}: {id:#?}", self.root)) } } @@ -425,8 +425,10 @@ pub(crate) fn crate_local_def_map(db: &dyn SourceDatabase, crate_id: Crate) -> D } #[salsa_macros::tracked(returns(ref))] -pub fn block_def_map(db: &dyn SourceDatabase, block_id: BlockId) -> DefMap { - let BlockLoc { ast_id, module } = *block_id.lookup(db); +pub fn block_def_map<'db>(db: &'db dyn SourceDatabase, block_id: BlockIdLt<'db>) -> DefMap { + let block_id = unsafe { block_id.to_static() }; + let ast_id = block_id.ast_id(db); + let module = unsafe { block_id.module(db).to_static() }; let visibility = Visibility::Module(module, VisibilityExplicitness::Implicit); let module_data = @@ -614,7 +616,7 @@ impl DefMap { /// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing /// the block, if `self` corresponds to a block expression. - pub fn containing_module(&self, local_mod: ModuleId) -> Option { + pub fn containing_module(&self, local_mod: ModuleIdLt<'_>) -> Option { match self[local_mod].parent { Some(parent) => Some(parent), None => self.block.map(|BlockInfo { parent, .. }| parent), @@ -725,11 +727,11 @@ impl DefMap { /// /// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns /// `None`, iteration continues. - pub(crate) fn with_ancestor_maps( + pub(crate) fn with_ancestor_maps<'db, T>( &self, - db: &dyn SourceDatabase, - local_mod: ModuleId, - f: &mut dyn FnMut(&DefMap, ModuleId) -> Option, + db: &'db dyn SourceDatabase, + local_mod: ModuleIdLt<'db>, + f: &mut dyn FnMut(&DefMap, ModuleIdLt<'db>) -> Option, ) -> Option { if let Some(it) = f(self, local_mod) { return Some(it); @@ -916,11 +918,13 @@ impl DerefMut for ModulesMap { } } -impl Index for ModulesMap { +impl Index> for ModulesMap { type Output = ModuleData; - fn index(&self, id: ModuleId) -> &ModuleData { - self.inner.get(&id).unwrap_or_else(|| panic!("ModuleId not found in ModulesMap: {id:#?}")) + fn index(&self, id: ModuleIdLt<'_>) -> &ModuleData { + self.inner + .get(&unsafe { id.to_static() }) + .unwrap_or_else(|| panic!("ModuleId not found in ModulesMap: {id:#?}")) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index a15d6d7474084..bb61cebd4f746 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -1877,7 +1877,6 @@ impl ModCollector<'_, '_> { } fn collect(&mut self, items: &[ModItemId], container: ItemContainerId) { - let krate = self.def_collector.def_map.krate; let is_crate_root = self.module_id == self.def_collector.def_map.root && self.def_collector.def_map.block.is_none(); @@ -1885,18 +1884,6 @@ impl ModCollector<'_, '_> { // for macros. self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone()); - // Prelude module is always considered to be `#[macro_use]`. - if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude { - // Don't insert macros from the prelude into blocks, as they can be shadowed by other macros. - if is_crate_root && prelude_module.krate(self.def_collector.db) != krate { - cov_mark::hit!(prelude_is_macro_use); - self.def_collector.import_macros_from_extern_crate( - prelude_module.krate(self.def_collector.db), - None, - None, - ); - } - } let db = self.def_collector.db; let module_id = self.module_id; let consider_deferred_derives = diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs index af7bd818f4e62..2f337a6ac8478 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs @@ -149,13 +149,7 @@ impl DirPath { if attr.starts_with("./") { attr = &attr["./".len()..]; } - let tmp; - let attr = if attr.contains('\\') { - tmp = attr.replace('\\', "/"); - &tmp - } else { - attr - }; + let attr = if attr.contains('\\') { &attr.replace('\\', "/") } else { attr }; let res = format!("{base}{attr}"); res } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index 7ffac38086729..fde1db4734a78 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -504,12 +504,10 @@ impl DefMap { ); } - let def_map; let module_data = if module.block(db) == self.block_id() { &self[module] } else { - def_map = module.def_map(db); - &def_map[module] + &module.def_map(db)[module] }; // Since it is a qualified path here, it should not contains legacy macros @@ -753,14 +751,7 @@ impl DefMap { fn resolve_in_prelude(&self, db: &dyn SourceDatabase, name: &Name) -> PerNs { if let Some((prelude, _use)) = self.prelude { - let keep; - let def_map = if prelude.krate(db) == self.krate { - self - } else { - // Extend lifetime - keep = prelude.def_map(db); - keep - }; + let def_map = if prelude.krate(db) == self.krate { self } else { prelude.def_map(db) }; def_map[prelude].scope.get(name) } else { PerNs::none() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index f073cf777dda7..75ec72801131a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -339,36 +339,19 @@ macro_rules! baz3 { () => { struct OkBaz3; } } } #[test] -fn prelude_is_macro_use() { - cov_mark::check!(prelude_is_macro_use); +fn prelude_is_not_macro_use() { check( r#" //- /main.rs edition:2018 crate:main deps:std structs!(Foo); -structs_priv!(Bar); -structs_outside!(Out); -crate::structs!(MacroNotResolved2); - -mod bar; - -//- /bar.rs -structs!(Baz); -crate::structs!(MacroNotResolved3); +structs_outside!(MacroNotResolved); //- /lib.rs crate:std pub mod prelude { pub mod rust_2018 { - #[macro_export] - macro_rules! structs { + pub macro structs { ($i:ident) => { struct $i; } } - - mod priv_mod { - #[macro_export] - macro_rules! structs_priv { - ($i:ident) => { struct $i; } - } - } } } @@ -379,13 +362,7 @@ macro_rules! structs_outside { "#, expect![[r#" crate - - Bar : type value - Foo : type value - - Out : type value - - bar : type - - crate::bar - - Baz : type value "#]], ); } @@ -743,12 +720,11 @@ foo!(); pub use core::foo; pub mod prelude { - pub mod rust_2018 {} + pub mod rust_2018 { + pub use crate::foo; + } } -#[macro_use] -mod std_macros; - //- /core.rs crate:core #[macro_export] macro_rules! foo { @@ -1779,3 +1755,18 @@ enum MyEnum {} "#]], ); } + +#[test] +fn regression_22806() { + compute_crate_def_map( + r#" +#![crate_type = "proc-macro"] + +fn foo() {} + +#[proc_macro] +fn foo() {} + "#, + |_| (), + ) +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 28d460b503fd6..fc639133d4c27 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -212,7 +212,7 @@ impl<'db> Resolver<'db> { let first_name = path.segments().first()?; let skip_to_mod = path.kind != PathKind::Plain; if skip_to_mod { - return self.module_scope.resolve_path_in_type_ns(db, path); + return self.skip_to_mod(|scope| scope.resolve_path_in_type_ns(db, path)); } let remaining_idx = || { @@ -326,6 +326,18 @@ impl<'db> Resolver<'db> { self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _)| it) } + fn skip_to_mod<'this, T>( + &'this self, + mut f: impl FnMut(&'this ModuleItemMap<'db>) -> Option, + ) -> Option { + self.scopes() + .find_map(|scope| match scope { + Scope::BlockScope(it) => f(it), + _ => None, + }) + .or_else(|| f(&self.module_scope)) + } + pub fn resolve_path_in_value_ns_with_prefix_info( &self, db: &dyn SourceDatabase, @@ -379,7 +391,7 @@ impl<'db> Resolver<'db> { let first_name = if path.is_self() { &tmp } else { path.segments().first()? }; let skip_to_mod = path.kind != PathKind::Plain && !path.is_self(); if skip_to_mod { - return self.module_scope.resolve_path_in_value_ns(db, path); + return self.skip_to_mod(|scope| scope.resolve_path_in_value_ns(db, path)); } if n_segments <= 1 { @@ -889,7 +901,7 @@ impl<'db> Resolver<'db> { resolver.scopes.push(Scope::ExprScope(ExprScope { owner, expr_scopes, scope_id })); if let Some(block) = expr_scopes.block(scope_id) { let def_map = block_def_map(db, block); - let local_def_map = block.lookup(db).module.only_local_def_map(db); + let local_def_map = block.module(db).only_local_def_map(db); resolver.scopes.push(Scope::BlockScope(ModuleItemMap { def_map, local_def_map, @@ -1095,7 +1107,7 @@ fn resolver_for_scope_<'db>( for scope in scope_chain.into_iter().rev() { if let Some(block) = scopes.block(scope) { let def_map = block_def_map(db, block); - let local_def_map = block.lookup(db).module.only_local_def_map(db); + let local_def_map = block.module(db).only_local_def_map(db); // Using `DefMap::ROOT` is okay here since inside modules other than the root, // there can't directly be expressions. r = r.push_block_scope(def_map, local_def_map, def_map.root); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 9126105530249..10a38ec71e372 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -21,8 +21,8 @@ use triomphe::Arc; use crate::{ ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, FxIndexMap, - HasModule, ImplId, ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, - UnionId, VariantId, + HasModule, ImplId, ItemContainerId, LoweringMode, ModuleId, StaticId, StructId, TraitId, + TypeAliasId, UnionId, VariantId, attrs::AttrFlags, expr_store::{ Body, ExpressionStore, ExpressionStoreBuilder, ExpressionStoreSourceMap, @@ -117,6 +117,7 @@ impl StructSignature { file_id, source.generic_param_list(), source.where_clause(), + LoweringMode::Analysis, ); ( Arc::new(StructSignature { @@ -190,6 +191,7 @@ impl UnionSignature { file_id, source.generic_param_list(), source.where_clause(), + LoweringMode::Analysis, ); ( Arc::new(UnionSignature { @@ -265,6 +267,7 @@ impl EnumSignature { file_id, source.generic_param_list(), source.where_clause(), + LoweringMode::Analysis, ); ( @@ -987,7 +990,7 @@ fn lower_fields( override_visibility: Option>, ) -> Option<(Arena, ExpressionStore, ExpressionStoreSourceMap)> { let cfg_options = module.krate(db).cfg_options(db); - let mut col = ExprCollector::new(db, module, fields.file_id); + let mut col = ExprCollector::new(db, module, fields.file_id, crate::LoweringMode::Analysis); let override_visibility = override_visibility.map(|vis| { LazyCell::new(|| { let span_map = fields.file_id.span_map(db); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index 0e4dd25c39df6..0598ab4a0435c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -68,7 +68,7 @@ impl Clone for TestDB { files: self.files.clone(), crates_map: self.crates_map.clone(), events: self.events.clone(), - nonce: Nonce::new(), + nonce: self.nonce, } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs index 632aa1b352a10..317be3432a549 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs @@ -8,8 +8,8 @@ use la_arena::ArenaMap; use syntax::ast::{self, HasVisibility}; use crate::{ - AssocItemId, HasModule, ItemContainerId, LocalFieldId, ModuleId, TraitId, VariantId, - nameres::DefMap, resolver::HasResolver, signatures::VariantFields, src::HasSource, + AssocItemId, HasModule, ItemContainerId, LocalFieldId, ModuleId, ModuleIdLt, TraitId, + VariantId, nameres::DefMap, resolver::HasResolver, signatures::VariantFields, src::HasSource, }; pub use crate::item_tree::{RawVisibility, VisibilityExplicitness}; @@ -40,9 +40,13 @@ impl Visibility { } #[tracing::instrument(skip_all)] - pub fn is_visible_from(self, db: &dyn SourceDatabase, from_module: ModuleId) -> bool { + pub fn is_visible_from<'db>( + self, + db: &'db dyn SourceDatabase, + from_module: ModuleIdLt<'db>, + ) -> bool { let to_module = match self { - Visibility::Module(m, _) => m, + Visibility::Module(m, _) => unsafe { m.to_db(db) }, Visibility::PubCrate(krate) => return from_module.krate(db) == krate, Visibility::Public => return true, }; @@ -58,11 +62,11 @@ impl Visibility { Self::is_visible_from_def_map_(db, def_map, to_module, from_module) } - pub(crate) fn is_visible_from_def_map( + pub(crate) fn is_visible_from_def_map<'db>( self, - db: &dyn SourceDatabase, - def_map: &DefMap, - from_module: ModuleId, + db: &'db dyn SourceDatabase, + def_map: &'db DefMap, + from_module: ModuleIdLt<'db>, ) -> bool { if cfg!(debug_assertions) { _ = def_map.modules[from_module]; @@ -88,11 +92,11 @@ impl Visibility { Self::is_visible_from_def_map_(db, def_map, to_module, from_module) } - fn is_visible_from_def_map_( - db: &dyn SourceDatabase, - def_map: &DefMap, - mut to_module: ModuleId, - mut from_module: ModuleId, + fn is_visible_from_def_map_<'db>( + db: &'db dyn SourceDatabase, + def_map: &'db DefMap, + mut to_module: ModuleIdLt<'db>, + mut from_module: ModuleIdLt<'db>, ) -> bool { debug_assert_eq!(to_module.krate(db), def_map.krate()); // `to_module` might be the root module of a block expression. Those have the same diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index ea0881923cb75..c9d3a87e96501 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -1,7 +1,9 @@ //! In certain situations, rust automatically inserts derefs as necessary: for //! example, field accesses `foo.bar` still work when `foo` is actually a //! reference to a type with the field `bar`. This is an approximation of the -//! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs). +//! logic in rustc (which lives in [`rustc_hir_typeck/autoderef.rs`]). +//! +//! [`rustc_hir_typeck/autoderef.rs`]: https://github.com/rust-lang/rust/blob/5503df87342a73d0c29126a7e08dc9c1255c46ad/compiler/rustc_hir_typeck/src/autoderef.rs use std::fmt; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index 65a910555d928..f82fc940ff636 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -68,14 +68,16 @@ pub(crate) fn generics_of<'db>( | BuiltinDeriveImplTrait::Ord | BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::Eq - | BuiltinDeriveImplTrait::PartialEq => Generics::from_generic_def(db, loc.adt.into()), + | BuiltinDeriveImplTrait::PartialEq => { + Generics::from_generic_def(db, loc.adt.into(), false) + } BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { let trait_id = loc .trait_ .get_id(interner.lang_items()) .expect("we don't pass the impl to the solver if we can't resolve the trait"); - let additional_param = coerce_pointee_new_type_param(trait_id).into(); - Generics::from_generic_def_plus_one(db, loc.adt.into(), additional_param) + let additional_param = coerce_pointee_new_type_param(trait_id); + Generics::from_generic_def_plus_one(db, loc.adt.into(), additional_param, false) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index a880ae5353b66..e58a22332cf81 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -413,6 +413,7 @@ pub(crate) fn create_anon_const<'a, 'db>( } } +#[salsa::tracked(cycle_result = const_eval_discriminant_cycle_result)] pub(crate) fn const_eval_discriminant_variant( db: &dyn HirDatabase, variant_id: EnumVariantId, @@ -449,7 +450,7 @@ pub(crate) fn const_eval_discriminant_variant( Ok(c) } -pub(crate) fn const_eval_discriminant_cycle_result( +fn const_eval_discriminant_cycle_result( _: &dyn HirDatabase, _: salsa::Id, _: EnumVariantId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 0e51594a78d06..546238f9bb309 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -2563,8 +2563,6 @@ fn const_transfer_memory() { } #[test] -// FIXME -#[should_panic] fn anonymous_const_block() { check_number( r#" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 1772e3c172413..96516d35d4d7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -224,6 +224,28 @@ fn const_eval_select() { ); } +#[test] +fn const_allocate() { + check_number( + r#" + //- minicore: fn + #[rustc_intrinsic] + pub const unsafe fn const_allocate(size: usize, align: usize) -> *mut u8; + #[rustc_intrinsic] + pub const unsafe fn const_deallocate(ptr: *mut u8, size: usize, align: usize); + + const GOAL: u8 = unsafe { + let ptr = const_allocate(4, 4); + *ptr = 5; + let value = *ptr; + const_deallocate(ptr, 4, 4); + value + }; + "#, + 5, + ); +} + #[test] fn wrapping_add() { check_number( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 6b482870ab823..c74ac51b61028 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -27,7 +27,7 @@ use crate::{ dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, lower::{GenericDefaults, TrackedStructToken, TypeAliasBounds}, - mir::{BorrowckResult, MirBody, MirLowerError}, + mir::{MirBody, MirLowerError}, next_solver::{ Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses, StoredEarlyBinder, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitRef, @@ -40,16 +40,16 @@ use crate::{ pub trait HirDatabase: SourceDatabase + std::fmt::Debug { // region:mir - // FXME: Collapse `mir_body_for_closure` into `mir_body` + // FIXME: Collapse `mir_body_for_closure` into `mir_body` // and `monomorphized_mir_body_for_closure` into `monomorphized_mir_body` #[salsa::transparent] fn mir_body(&self, def: InferBodyId) -> Result<&MirBody, MirLowerError> { - crate::mir::mir_body_query(self, def).as_ref().map_err(|err| err.clone()) + crate::mir::mir_body_query(self, def).map_err(|err| err.clone()) } #[salsa::transparent] fn mir_body_for_closure(&self, def: InternedClosureId) -> Result<&MirBody, MirLowerError> { - crate::mir::mir_body_for_closure_query(self, def).as_ref().map_err(|err| err.clone()) + crate::mir::mir_body_for_closure_query(self, def).map_err(|err| err.clone()) } #[salsa::transparent] @@ -59,9 +59,7 @@ pub trait HirDatabase: SourceDatabase + std::fmt::Debug { subst: StoredGenericArgs, env: StoredParamEnvAndCrate, ) -> Result<&MirBody, MirLowerError> { - crate::mir::monomorphized_mir_body_query(self, def, subst, env) - .as_ref() - .map_err(|err| err.clone()) + crate::mir::monomorphized_mir_body_query(self, def, subst, env).map_err(|err| err.clone()) } #[salsa::transparent] @@ -72,15 +70,9 @@ pub trait HirDatabase: SourceDatabase + std::fmt::Debug { env: StoredParamEnvAndCrate, ) -> Result<&MirBody, MirLowerError> { crate::mir::monomorphized_mir_body_for_closure_query(self, def, subst, env) - .as_ref() .map_err(|err| err.clone()) } - #[salsa::transparent] - fn borrowck(&self, def: InferBodyId) -> Result<&[BorrowckResult], MirLowerError> { - crate::mir::borrowck_query(self, def).as_ref().map(|it| &**it).map_err(|err| err.clone()) - } - #[salsa::invoke(crate::consteval::const_eval)] #[salsa::transparent] fn const_eval<'db>( @@ -104,7 +96,7 @@ pub trait HirDatabase: SourceDatabase + std::fmt::Debug { fn const_eval_static<'db>(&'db self, def: StaticId) -> Result, ConstEvalError>; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] - #[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)] + #[salsa::transparent] fn const_eval_discriminant(&self, def: EnumVariantId) -> Result; #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] @@ -119,7 +111,7 @@ pub trait HirDatabase: SourceDatabase + std::fmt::Debug { // endregion:mir #[salsa::invoke(crate::layout::layout_of_adt_query)] - #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_cycle_result)] + #[salsa::transparent] fn layout_of_adt( &self, def: AdtId, @@ -128,7 +120,7 @@ pub trait HirDatabase: SourceDatabase + std::fmt::Debug { ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] - #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_cycle_result)] + #[salsa::transparent] fn layout_of_ty( &self, ty: StoredTy, @@ -137,7 +129,7 @@ pub trait HirDatabase: SourceDatabase + std::fmt::Debug { #[salsa::transparent] fn target_data_layout(&self, krate: Crate) -> Result<&TargetDataLayout, TargetLoadError> { - crate::layout::target_data_layout_query(self, krate).as_ref().map_err(|err| err.clone()) + crate::layout::target_data_layout_query(self, krate).map_err(|err| err.clone()) } #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 5d0a010a7fe39..c5ce3af3a9ac9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -130,6 +130,7 @@ pub struct HirFormatter<'a, 'db> { pub entity_limit: Option, /// When rendering functions, whether to show the constraint from the container show_container_bounds: bool, + render_private_fields: bool, omit_verbose_types: bool, closure_style: ClosureStyle, display_lifetimes: DisplayLifetime, @@ -263,6 +264,7 @@ pub trait HirDisplay<'db> { display_kind, closure_style, show_container_bounds, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -287,6 +289,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Diagnostics, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -312,6 +315,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Diagnostics, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -337,6 +341,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Diagnostics, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -364,6 +369,7 @@ pub trait HirDisplay<'db> { display_target: DisplayTarget::from_crate(db, module_id.krate(db)), display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque }, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, currently_formatting_bounds: Default::default(), trait_bounds_need_parens: false, @@ -394,6 +400,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Test, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::Always, } } @@ -419,6 +426,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Diagnostics, show_container_bounds, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -495,6 +503,10 @@ impl<'db> HirFormatter<'_, 'db> { pub fn show_container_bounds(&self) -> bool { self.show_container_bounds } + + pub fn render_private_fields(&self) -> bool { + self.render_private_fields + } } #[derive(Debug, Clone, Copy)] @@ -566,6 +578,7 @@ pub struct HirDisplayWrapper<'a, 'db, T> { display_kind: DisplayKind, display_target: DisplayTarget, show_container_bounds: bool, + render_private_fields: bool, display_lifetimes: DisplayLifetime, } @@ -601,6 +614,7 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> { display_target: self.display_target, closure_style: self.closure_style, show_container_bounds: self.show_container_bounds, + render_private_fields: self.render_private_fields, display_lifetimes: self.display_lifetimes, currently_formatting_bounds: Default::default(), trait_bounds_need_parens: false, @@ -616,6 +630,11 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> { self.display_lifetimes = l; self } + + pub fn with_private_fields(mut self, render: bool) -> Self { + self.render_private_fields = render; + self + } } impl<'db, T> fmt::Display for HirDisplayWrapper<'_, 'db, T> @@ -2304,10 +2323,10 @@ impl<'db> HirDisplay<'db> for Region<'db> { Ok(()) } RegionKind::ReBound(BoundVarIndexKind::Bound(db), idx) => { - write!(f, "?{}.{}", db.as_u32(), idx.var.as_u32()) + write!(f, "'?{}.{}", db.as_u32(), idx.var.as_u32()) } RegionKind::ReBound(BoundVarIndexKind::Canonical, idx) => { - write!(f, "?c.{}", idx.var.as_u32()) + write!(f, "'?c.{}", idx.var.as_u32()) } RegionKind::ReVar(_) => write!(f, "_"), RegionKind::ReStatic => write!(f, "'static"), @@ -2319,8 +2338,8 @@ impl<'db> HirDisplay<'db> for Region<'db> { } } RegionKind::ReErased => write!(f, "'"), - RegionKind::RePlaceholder(_) => write!(f, ""), - RegionKind::ReLateParam(_) => write!(f, ""), + RegionKind::RePlaceholder(_) => write!(f, "'"), + RegionKind::ReLateParam(_) => write!(f, "'_"), } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 34858212cb3ec..9ee39b3abe649 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -395,7 +395,7 @@ where } fn receiver_is_dispatchable<'db>( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, trait_: TraitId, func: FunctionId, sig: &EarlyBinder<'db, Binder<'db, rustc_type_ir::FnSig>>>, @@ -417,9 +417,7 @@ fn receiver_is_dispatchable<'db>( return true; } - let Some(&receiver_ty) = sig.inputs().skip_binder().first() else { - return false; - }; + let receiver_ty = interner.liberate_late_bound_regions(func.into(), sig.input(0)); let lang_items = interner.lang_items(); let traits = (lang_items.Unsize, lang_items.DispatchFromDyn); @@ -451,7 +449,7 @@ fn receiver_is_dispatchable<'db>( TraitRef::new(interner, unsize_did.into(), [self_param_ty, unsized_self_ty]); // U: Trait - let args = GenericArgs::for_item(interner, trait_.into(), |index, kind, _| { + let args = GenericArgs::for_item(interner, trait_.into(), |index, kind, _, _| { if index == 0 { unsized_self_ty.into() } else { mk_param(interner, index, kind) } }); let trait_predicate = TraitRef::new_from_args(interner, trait_.into(), args); @@ -487,9 +485,10 @@ fn receiver_for_self_ty<'db>( receiver_ty: Ty<'db>, self_ty: Ty<'db>, ) -> Ty<'db> { - let args = GenericArgs::for_item(interner, SolverDefId::FunctionId(func), |index, kind, _| { - if index == 0 { self_ty.into() } else { mk_param(interner, index, kind) } - }); + let args = + GenericArgs::for_item(interner, SolverDefId::FunctionId(func), |index, kind, _, _| { + if index == 0 { self_ty.into() } else { mk_param(interner, index, kind) } + }); EarlyBinder::bind(receiver_ty).instantiate(interner, args).skip_norm_wip() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index 1f98fcb46642d..f2ca060bb5335 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -72,17 +72,31 @@ impl<'db> SingleGenerics<'db> { self.params.len_lifetimes() } - pub(crate) fn len(&self) -> usize { - self.params.len() + pub(crate) fn len(&self, consider_late_bound: bool) -> usize { + if consider_late_bound { + self.params.len() + } else { + self.params.len() - self.params.len_late_bound_lifetimes() + } } fn iter_lifetimes(&self) -> impl Iterator { let parent = self.def; self.params - .iter_lt() + .iter_early_bound_lt() .map(move |(local_id, data)| (LifetimeParamId { parent, local_id }, data)) } + fn iter_late_bound_lifetimes( + &self, + consider_late_bound: bool, + ) -> impl Iterator { + let parent = self.def; + self.params.iter_late_bound_lt().filter_map(move |(local_id, data)| { + consider_late_bound.then_some((LifetimeParamId { parent, local_id }, data)) + }) + } + pub(crate) fn iter_type_or_consts( &self, ) -> impl Iterator { @@ -118,23 +132,46 @@ impl<'db> SingleGenerics<'db> { (trait_self, iter) } - pub(crate) fn iter(&self) -> impl Iterator)> { - let lifetimes = self.iter_lifetimes().map(|(id, data)| { + pub(crate) fn iter( + &self, + consider_late_bound: bool, + ) -> impl Iterator)> { + let lifetime_map = |(id, data)| { (GenericParamId::LifetimeParamId(id), GenericParamDataRef::LifetimeParamData(data)) - }); + }; + let lifetimes = self.iter_lifetimes().map(lifetime_map); + let late_bound_lifetimes = + self.iter_late_bound_lifetimes(consider_late_bound).map(lifetime_map); + let (trait_self, type_and_consts) = self.trait_self_and_others(); - trait_self.into_iter().chain(lifetimes).chain(type_and_consts) + trait_self.into_iter().chain(lifetimes).chain(type_and_consts).chain(late_bound_lifetimes) } pub(crate) fn iter_with_idx( &self, ) -> impl Iterator)> { - std::iter::zip(self.preceding_params_len.., self.iter()) + std::iter::zip(self.preceding_params_len.., self.iter(false)) .map(|(index, (id, data))| (index, id, data)) } - pub(crate) fn iter_id(&self) -> impl Iterator { - self.iter().map(|(id, _)| id) + pub(crate) fn iter_id( + &self, + consider_late_bound: bool, + ) -> impl Iterator { + self.iter(consider_late_bound).map(|(id, _)| id) + } + + pub(crate) fn iter_late_bound( + &self, + ) -> impl Iterator)> { + // we don't handle late bound types or const now, so it is ignored for now + let parent = self.def; + self.params.iter_late_bound_lt().map(move |(local_id, data)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }), + GenericParamDataRef::LifetimeParamData(data), + ) + }) } } @@ -169,7 +206,7 @@ impl<'db> Generics<'db> { pub(crate) fn iter_self( &self, ) -> impl Iterator)> { - self.owner().iter() + self.owner().iter(false) } pub(crate) fn iter_self_with_idx( @@ -178,8 +215,14 @@ impl<'db> Generics<'db> { self.owner().iter_with_idx() } + pub(crate) fn iter_self_late_bound( + &self, + ) -> impl Iterator)> { + self.owner().iter_late_bound() + } + pub(crate) fn iter_parent_id(&self) -> impl Iterator { - self.parent().into_iter().flat_map(|parent| parent.iter_id()) + self.parent().into_iter().flat_map(move |parent| parent.iter_id(false)) } pub(crate) fn iter_self_type_or_consts( @@ -189,27 +232,33 @@ impl<'db> Generics<'db> { } /// Iterate over the parent params followed by self params. - #[cfg(test)] - pub(crate) fn iter(&self) -> impl Iterator)> { - self.iter_owners().flat_map(|owner| owner.iter()) + pub(crate) fn iter( + &self, + consider_late_bound: bool, + ) -> impl Iterator)> { + self.iter_owners().flat_map(move |owner| owner.iter(consider_late_bound)) } - pub(crate) fn iter_id(&self) -> impl Iterator { - self.iter_owners().flat_map(|owner| owner.iter_id()) + pub(crate) fn iter_id( + &self, + consider_late_bound: bool, + ) -> impl Iterator { + self.iter_owners().flat_map(move |owner| owner.iter_id(consider_late_bound)) } /// Returns total number of generic parameters in scope, including those from parent. - pub(crate) fn len(&self) -> usize { + pub(crate) fn len(&self, consider_late_bound: bool) -> usize { match &*self.chain { - [parent, owner] => parent.len() + owner.len(), - [owner] => owner.len(), + [parent, owner] => parent.len(consider_late_bound) + owner.len(consider_late_bound), + [owner] => owner.len(consider_late_bound), _ => unreachable!(), } } #[inline] pub(crate) fn len_parent(&self) -> usize { - self.parent().map_or(0, SingleGenerics::len) + // add `consider_late_bound` arg if needed in future, currently it's not needed. + self.parent().map_or(0, |p| p.len(true)) } pub(crate) fn len_lifetimes_self(&self) -> usize { @@ -275,12 +324,30 @@ impl<'db> Generics<'db> { } } - pub(crate) fn lifetime_param_idx(&self, param: LifetimeParamId) -> u32 { + // Rename this? + pub(crate) fn lifetime_param_idx( + &self, + param: LifetimeParamId, + is_lowering_impl_trait_bounds: bool, + ) -> (u32, bool) { let owner = self.find_owner(param.parent); + if is_lowering_impl_trait_bounds { + let idx = self.opaque_lifetime_idx(param); + return (owner.preceding_params_len + (idx as u32), false); + } + let has_trait_self = matches!(owner.def, GenericDefId::TraitId(_)); - owner.preceding_params_len - + u32::from(has_trait_self) - + param.local_id.into_raw().into_u32() + match owner.params.lifetime_param_idx(¶m.local_id) { + Some((idx, is_late_bound)) => { + let idx = if is_late_bound { + idx as u32 + } else { + owner.preceding_params_len + u32::from(has_trait_self) + (idx as u32) + }; + (idx, is_late_bound) + } + _ => unreachable!(), + } } #[deprecated = "don't use this; it's easy to expose an erroneous `Generics` with this"] @@ -294,6 +361,18 @@ impl<'db> Generics<'db> { }); Generics { chain } } + + fn opaque_lifetime_idx(&self, param: LifetimeParamId) -> usize { + self.find_owner(param.parent) + .iter_id(true) + .position(|id| { + let GenericParamId::LifetimeParamId(id) = id else { + return false; + }; + param == id + }) + .unwrap() + } } pub(crate) struct ProvenanceSplit { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 116469a554204..ebcc0bd3f6f57 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -2,7 +2,7 @@ //! the type of each expression and pattern. //! //! For type inference, compare the implementations in rustc (the various -//! check_* methods in rustc_hir_analysis/check/mod.rs are a good entry point) and +//! check_* methods in [`rustc_hir_typeck/check.rs`] are a good entry point) and //! IntelliJ-Rust (org.rust.lang.core.types.infer). Our entry point for //! inference here is the `infer` function, which infers the types of all //! expressions in a given function. @@ -12,6 +12,8 @@ //! we might determine that certain variables need to be equal to each other, or //! to certain types. To record this, we use the union-find implementation from //! the `ena` crate, which is extracted from rustc. +//! +//! [`rustc_hir_typeck/check.rs`]: https://github.com/rust-lang/rust/blob/5503df87342a73d0c29126a7e08dc9c1255c46ad/compiler/rustc_hir_typeck/src/check.rs mod autoderef; mod callee; @@ -91,8 +93,8 @@ use crate::{ unify::resolve_completely::WriteBackCtxt, }, lower::{ - ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, LoweringMode, - diagnostics::TyLoweringDiagnostic, + ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, LifetimeLoweringMode, + LoweringMode, diagnostics::TyLoweringDiagnostic, }, method_resolution::CandidateId, next_solver::{ @@ -522,6 +524,18 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, + ReturnOutsideFunction { + #[type_visitable(ignore)] + expr: ExprId, + #[type_visitable(ignore)] + kind: ReturnKind, + }, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ReturnKind { + ReturnExpr, + BecomeExpr, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -1932,6 +1946,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.allow_using_generic_params, infer_vars, &self.defined_anon_consts, + LifetimeLoweringMode::LateParam, ); f(&mut ctx) } @@ -2008,9 +2023,10 @@ impl<'body, 'db> InferenceContext<'body, 'db> { && let GeneralConstId::AnonConstId(konst) = konst.def.0 { self.defined_anon_consts.borrow_mut().push(konst); + } else { + self.write_expr_ty(expr, expected_ty); } - self.write_expr_ty(expr, expected_ty); // FIXME: Report an error if needed. konst.unwrap_or_else(|_| self.table.next_const_var(Span::Dummy)) } @@ -2266,6 +2282,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.allow_using_generic_params, Some(&mut vars_ctx), &self.defined_anon_consts, + LifetimeLoweringMode::LateParam, ); if let Some(type_anchor) = path.type_anchor() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index ab111736d56a1..2a5567dfae544 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -304,7 +304,7 @@ impl<'db> InferenceContext<'_, 'db> { }; // Now go through the argument patterns - for (arg_pat, arg_ty) in args.iter().zip(bound_sig.skip_binder().inputs()) { + for (arg_pat, arg_ty) in args.iter().zip(liberated_sig.inputs()) { self.infer_top_pat(*arg_pat, *arg_ty, PatOrigin::Param); } @@ -1148,8 +1148,9 @@ impl<'db> InferenceContext<'_, 'db> { } fn closure_sigs(&self, bound_sig: PolyFnSig<'db>) -> ClosureSignatures<'db> { - let liberated_sig = bound_sig.skip_binder(); - // FIXME: When we lower HRTB we'll need to actually liberate regions here. + // TODO: def id needs to be changed? + let liberated_sig = + self.interner().liberate_late_bound_regions(self.owner.into(), bound_sig); ClosureSignatures { bound_sig, liberated_sig } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index dd0efea4d754b..e871edd265c16 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -14,6 +14,7 @@ use la_arena::{Idx, RawIdx}; use rustc_hash::FxHashMap; use thin_vec::ThinVec; +use crate::lower::LifetimeLoweringMode; use crate::{ InferenceDiagnostic, InferenceTyDiagnosticSource, Span, TyLoweringDiagnostic, db::{AnonConstId, HirDatabase}, @@ -107,6 +108,7 @@ impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> { allow_using_generic_params: bool, infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>, defined_anon_consts: &'a RefCell>, + lifetime_lowering_mode: LifetimeLoweringMode, ) -> Self { let mut ctx = TyLoweringContext::new( db, @@ -116,6 +118,7 @@ impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> { generic_def, generics, lifetime_elision, + lifetime_lowering_mode, ) .with_infer_vars_behavior(infer_vars); if !allow_using_generic_params { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 3356c4d78ae88..df17cc38774a8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -43,7 +43,7 @@ use crate::{ }; use super::{ - BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, + BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, ReturnKind, cast::CastCheck, find_breakable, }; @@ -576,7 +576,7 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } &Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr), - &Expr::Become { expr } => self.infer_expr_become(expr), + &Expr::Become { expr } => self.infer_expr_become(tgt_expr, expr), Expr::Yield { expr } => { if let Some((resume_ty, yield_ty)) = self.resume_yield_tys { if let Some(expr) = expr { @@ -1454,7 +1454,10 @@ impl<'db> InferenceContext<'_, 'db> { } } None => { - // FIXME: diagnose return outside of function + self.push_diagnostic(InferenceDiagnostic::ReturnOutsideFunction { + expr: ret, + kind: ReturnKind::ReturnExpr, + }); if let Some(expr) = expr { self.infer_expr_no_expect(expr, ExprIsRead::Yes); } @@ -1463,7 +1466,7 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } - fn infer_expr_become(&mut self, expr: ExprId) -> Ty<'db> { + fn infer_expr_become(&mut self, tgt_expr: ExprId, expr: ExprId) -> Ty<'db> { match &self.return_coercion { Some(return_coercion) => { let ret_ty = return_coercion.expected_ty(); @@ -1476,7 +1479,10 @@ impl<'db> InferenceContext<'_, 'db> { _ = self.demand_eqtype(expr.into(), call_expr_ty, ret_ty); } None => { - // FIXME: diagnose `become` outside of functions + self.push_diagnostic(InferenceDiagnostic::ReturnOutsideFunction { + expr: tgt_expr, + kind: ReturnKind::BecomeExpr, + }); self.infer_expr_no_expect(expr, ExprIsRead::Yes); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index 9119af9628eb9..85801358d4441 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs @@ -333,7 +333,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let args = GenericArgs::for_item( self.interner(), trait_did.into(), - |param_idx, param_id, _| match param_id { + |param_idx, param_id, _, _| match param_id { GenericParamId::LifetimeParamId(_) | GenericParamId::ConstParamId(_) => { unreachable!("did not expect operand trait to have lifetime/const args") } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 0ec72edc3d59c..ecc81f9de0232 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -15,7 +15,7 @@ use crate::{ infer::{ InferenceTyLoweringVarsCtx, diagnostics::InferenceTyLoweringContext as TyLoweringContext, }, - lower::{GenericPredicates, LifetimeElisionKind}, + lower::{GenericPredicates, LifetimeElisionKind, LifetimeLoweringMode}, method_resolution::{self, CandidateId, MethodError}, next_solver::{ GenericArg, GenericArgs, TraitRef, Ty, Unnormalized, infer::traits::ObligationCause, @@ -166,6 +166,7 @@ impl<'db> InferenceContext<'_, 'db> { self.allow_using_generic_params, Some(&mut vars_ctx), &self.defined_anon_consts, + LifetimeLoweringMode::LateParam, ); let mut path_ctx = if no_diagnostics { ctx.at_path_forget_diagnostics(path) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index ed4775539a10f..adba047042cb6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -32,7 +32,6 @@ use crate::{ traits::StoredParamEnvAndCrate, }; -pub(crate) use self::adt::layout_of_adt_cycle_result; pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; pub(crate) mod adt; @@ -161,6 +160,7 @@ fn layout_of_simd_ty<'db>( Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?)) } +#[salsa::tracked(cycle_result = layout_of_ty_cycle_result)] pub fn layout_of_ty_query( db: &dyn HirDatabase, ty: StoredTy, @@ -503,7 +503,7 @@ pub fn layout_of_ty_query( Ok(Arc::new(result)) } -pub(crate) fn layout_of_ty_cycle_result( +fn layout_of_ty_cycle_result( _: &dyn HirDatabase, _: salsa::Id, _: StoredTy, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 22dd53ca2dd01..1f321af79493f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -19,6 +19,7 @@ use crate::{ traits::StoredParamEnvAndCrate, }; +#[salsa::tracked(cycle_result = layout_of_adt_cycle_result)] pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId, @@ -96,7 +97,7 @@ pub fn layout_of_adt_query( Ok(Arc::new(result)) } -pub(crate) fn layout_of_adt_cycle_result( +fn layout_of_adt_cycle_result( _: &dyn HirDatabase, _: salsa::Id, _def: AdtId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index 26fa73e76bc3c..cf92c18f8ccd8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -6,7 +6,7 @@ use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutError}; use crate::db::HirDatabase; -#[salsa_macros::tracked(returns(ref))] +#[salsa_macros::tracked(returns(as_ref))] pub fn target_data_layout_query( db: &dyn HirDatabase, krate: Crate, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 964eb2abc3c98..a9975615be968 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -106,13 +106,13 @@ pub use autoderef::autoderef; pub use infer::{ Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, - PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, + PointerCast, ReturnKind, cast::CastError, could_coerce, could_unify, could_unify_deeply, infer_query_with_inspect, }; pub use lower::{ FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, - LifetimeElisionKind, LoweringMode, TyDefId, TyLoweringContext, TyLoweringInferVarsCtx, - TyLoweringResult, ValueTyDefId, diagnostics::*, + LifetimeElisionKind, LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext, + TyLoweringInferVarsCtx, TyLoweringResult, ValueTyDefId, diagnostics::*, }; pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; pub use target_feature::TargetFeatures; @@ -221,7 +221,7 @@ pub fn type_or_const_param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> } pub fn lifetime_param_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> u32 { - generics::generics(db, id.parent).lifetime_param_idx(id) + generics::generics(db, id.parent).lifetime_param_idx(id, false).0 } #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 4f6943f695a8b..e251fa84ed1a1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -44,7 +44,8 @@ use rustc_abi::ExternAbi; use rustc_ast_ir::Mutability; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, BoundVarIndexKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, + AliasTyKind, BoundRegion, BoundRegionKind, BoundTyKind, BoundVar, BoundVarIndexKind, + BoundVariableKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate, inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, @@ -54,6 +55,9 @@ use stdx::{impl_from, never}; use thin_vec::ThinVec; use tracing::debug; +pub use hir_def::LoweringMode; +pub(crate) use hir_def::TrackedStructToken; + use crate::{ ImplTraitId, Span, TyLoweringDiagnostic, consteval::{create_anon_const, path_to_const}, @@ -61,12 +65,13 @@ use crate::{ generics::{Generics, SingleGenerics, generics}, infer::unify::InferenceTable, next_solver::{ - AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind, - DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, - FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig, - Predicate, Region, StoredClauses, StoredConst, StoredEarlyBinder, StoredGenericArg, - StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, - Tys, Unnormalized, abi::Safety, util::BottomUpFolder, + AliasTy, Binder, BoundExistentialPredicates, BoundVarKinds, Clause, ClauseKind, Clauses, + Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, + FnSigKind, FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, + PolyFnSig, Predicate, Region, StoredClauses, StoredConst, StoredEarlyBinder, + StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, + TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, mk_param, + util::BottomUpFolder, }, }; @@ -199,33 +204,6 @@ pub trait TyLoweringInferVarsCtx<'db> { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum LoweringMode { - Analysis, - Ide, -} - -pub(crate) use self::tracked_struct_token::TrackedStructToken; -mod tracked_struct_token { - use super::LoweringMode; - - /// A token that is required to construct tracked structs. - /// This exists to prevent one from accidentally creating a tracked struct outside of a query which may happen for some codepaths. - pub(crate) struct TrackedStructToken { - // #[non_exhaustive] doesn't work for us here, we want it module focused. - _private: (), - } - - impl LoweringMode { - pub(crate) fn allow_tracked_structs(self) -> Option { - match self { - LoweringMode::Analysis => Some(TrackedStructToken { _private: () }), - LoweringMode::Ide => None, - } - } - } -} - pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, pub(crate) interner: DbInterner<'db>, @@ -247,6 +225,9 @@ pub struct TyLoweringContext<'db, 'a> { forbid_params_after_reason: ForbidParamsAfterReason, pub(crate) defined_anon_consts: ThinVec, infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>, + is_lowering_impl_trait_bounds: bool, + bound_vars: Vec>, // FIXME: HRTB and other for lifetime doesn't change it now + lifetime_lowering_mode: LifetimeLoweringMode, } impl<'db, 'a> TyLoweringContext<'db, 'a> { @@ -258,10 +239,12 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { generic_def: GenericDefId, generics: &'a OnceCell>, lifetime_elision: LifetimeElisionKind<'db>, + lifetime_lowering_mode: LifetimeLoweringMode, ) -> Self { let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); let in_binders = DebruijnIndex::ZERO; let interner = DbInterner::new_with(db, resolver.krate()); + let bound_vars = vec![BoundVarKinds::empty(interner)]; Self { db, // Can provide no block since we don't use it for trait solving. @@ -283,6 +266,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { forbid_params_after_reason: ForbidParamsAfterReason::AnonConst, defined_anon_consts: ThinVec::new(), infer_vars: None, + is_lowering_impl_trait_bounds: false, + bound_vars, + lifetime_lowering_mode, } } @@ -385,6 +371,32 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } } + + fn peek_bound_vars(&self) -> BoundVarKinds<'db> { + *self.bound_vars.last().unwrap() + } + + fn bound_vars( + db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + def: GenericDefId, + generic: &'a OnceCell>, + ) -> BoundVarKinds<'db> { + let def_id = def.into(); + + let generics = generic.get_or_init(|| generics(db, def)); + let args = generics.iter_self_late_bound().map(|(_, data)| match data { + GenericParamDataRef::TypeParamData(..) => { + BoundVariableKind::Ty(BoundTyKind::Param(def_id)) + } + GenericParamDataRef::ConstParamData(..) => BoundVariableKind::Const, + GenericParamDataRef::LifetimeParamData(..) => { + BoundVariableKind::Region(BoundRegionKind::Named(def_id)) + } + }); + + BoundVarKinds::new_from_iter(interner, args) + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] @@ -399,6 +411,16 @@ pub(crate) enum ImplTraitLoweringMode { Disallowed, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum LifetimeLoweringMode { + /// Lowers the late bound lifetimes to `ReBound`, used in cases when lowering + /// from outside of function. + Bound, + /// Lowers the late bound lifetimes to `ReLateParam`, used in cases when lowering + /// inside the function itself + LateParam, +} + impl<'db, 'a> TyLoweringContext<'db, 'a> { pub fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { self.lower_ty_ext(type_ref).0 @@ -471,12 +493,40 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } - fn region_param(&mut self, id: LifetimeParamId, index: u32) -> Region<'db> { + fn region_param( + &mut self, + id: LifetimeParamId, + index: u32, + is_late_bound: bool, + ) -> Region<'db> { if self.param_index_is_disallowed(index) { // FIXME: Report an error. self.types.regions.error } else { - Region::new_early_param(self.interner, EarlyParamRegion { id, index }) + if is_late_bound { + if self.lifetime_lowering_mode == LifetimeLoweringMode::Bound { + Region::new_bound( + self.interner, + self.in_binders, + BoundRegion { + var: BoundVar::from_u32(index), + kind: BoundRegionKind::Named(id.parent.into()), + }, + ) + } else { + let solver_def_id = id.parent.into(); + Region::new_late_param( + self.interner, + solver_def_id, + BoundRegion { + var: BoundVar::from_u32(index), + kind: BoundRegionKind::Named(solver_def_id), + }, + ) + } + } else { + Region::new_early_param(self.interner, EarlyParamRegion { id, index }) + } } } @@ -568,8 +618,45 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { }); self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data; - let args = - GenericArgs::identity_for_item(self.interner, opaque_ty_id.into()); + let mut late_bound_index = 0; + let args = GenericArgs::for_item( + self.interner, + opaque_ty_id.into(), + |index, param_id, lt_param, _| { + if let Some(lt) = lt_param + && lt.is_late_bound() + && !self.is_lowering_impl_trait_bounds + { + let GenericParamId::LifetimeParamId(id) = param_id else { + unreachable!() + }; + let bound_region_kind = + BoundRegionKind::Named(id.parent.into()); + let region = match self.lifetime_lowering_mode { + LifetimeLoweringMode::Bound => Region::new_bound( + interner, + self.in_binders, + BoundRegion { + var: BoundVar::from_u32(late_bound_index), + kind: bound_region_kind, + }, + ), + LifetimeLoweringMode::LateParam => Region::new_late_param( + interner, + self.generic_def.into(), + BoundRegion { + var: BoundVar::from_u32(late_bound_index), + kind: bound_region_kind, + }, + ), + }; + late_bound_index += 1; + return region.into(); + } + + mk_param(interner, index - late_bound_index, param_id) + }, + ); Ty::new_alias( self.interner, AliasTy::new_from_args( @@ -635,17 +722,23 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { args.push(ctx.lower_ty(ret_ty)); }); self.lifetime_elision = old_lifetime_elision; + + // FIXME: When we don't drop HRTB lifetimes, use those here. + let binder = BoundVarKinds::empty(interner); Ty::new_fn_ptr( interner, - Binder::dummy(FnSig { - fn_sig_kind: FnSigKind::new( - fn_.abi, - if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, - fn_.is_varargs, - // FIXME(splat): handle splatted arguments - ), - inputs_and_output: Tys::new_from_slice(&args), - }), + Binder::bind_with_vars( + FnSig { + fn_sig_kind: FnSigKind::new( + fn_.abi, + if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, + fn_.is_varargs, + // FIXME(splat): handle splatted arguments + ), + inputs_and_output: Tys::new_from_slice(&args), + }, + binder, + ), ) } @@ -792,6 +885,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let mut clause = None; match bound { &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => { + let binder = self.peek_bound_vars(); // FIXME Don't silently drop the hrtb lifetimes here if let Some((trait_ref, mut ctx)) = self.lower_trait_ref_from_path(path, self_ty) { // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented @@ -810,12 +904,15 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } clause = Some(Clause(Predicate::new( interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), + Binder::bind_with_vars( + rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + ), + binder, + ), ))); } } @@ -834,13 +931,17 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } &TypeBound::Lifetime(l) => { let lifetime = self.lower_lifetime(l); + let binder = self.peek_bound_vars(); clause = Some(Clause(Predicate::new( self.interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::TypeOutlives(OutlivesPredicate( - self_ty, lifetime, - )), - )), + Binder::bind_with_vars( + rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::TypeOutlives(OutlivesPredicate( + self_ty, lifetime, + )), + ), + binder, + ), ))); } TypeBound::Use(_) | TypeBound::Error => {} @@ -1107,7 +1208,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { rustc_type_ir::RegionKind::ReBound(BoundVarIndexKind::Bound(db), var) => { Region::new_bound( self.interner, - db.shifted_out_to_binder(DebruijnIndex::from_u32(2)), + db.shifted_out_to_binder(DebruijnIndex::from_u32(1)), var, ) } @@ -1131,6 +1232,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.interner, AliasTy::new_from_args(interner, rustc_type_ir::Opaque { def_id: def_id.into() }, args), ); + let prev_is_lowering_impl_trait_bounds = + mem::replace(&mut self.is_lowering_impl_trait_bounds, true); let (predicates, assoc_ty_bounds_start) = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { let mut predicates = Vec::new(); @@ -1170,6 +1273,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { (predicates, assoc_ty_bounds_start) }); + self.is_lowering_impl_trait_bounds = prev_is_lowering_impl_trait_bounds; ImplTrait { predicates: Clauses::new_from_slice(&predicates).store(), assoc_ty_bounds_start, @@ -1181,8 +1285,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { Some(resolution) => match resolution { LifetimeNs::Static => Region::new_static(self.interner), LifetimeNs::LifetimeParam(id) => { - let idx = self.generics().lifetime_param_idx(id); - self.region_param(id, idx) + let (idx, is_late_bound) = + self.generics().lifetime_param_idx(id, self.is_lowering_impl_trait_bounds); + self.region_param(id, idx, is_late_bound) } }, None => Region::error(self.interner), @@ -1300,6 +1405,7 @@ pub(crate) fn impl_trait_with_diagnostics( impl_id.into(), &generics, LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + LifetimeLoweringMode::Bound, ); let self_ty = db.impl_self_ty(impl_id).skip_binder(); let target_trait = impl_data.target_trait.as_ref()?; @@ -1384,6 +1490,7 @@ impl ImplTraits { def.into(), &generics, LifetimeElisionKind::Infer, + LifetimeLoweringMode::Bound, ) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); if let Some(ret_type) = data.ret_type { @@ -1415,6 +1522,7 @@ impl ImplTraits { def.into(), &generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); if let Some(type_ref) = data.ty { @@ -1516,6 +1624,7 @@ pub(crate) fn type_for_const_with_diagnostics( def.into(), &generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ); ctx.set_lifetime_elision(LifetimeElisionKind::for_const(ctx.interner, parent)); let result = StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store()); @@ -1546,6 +1655,7 @@ pub(crate) fn type_for_static_with_diagnostics( def.into(), &generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ); ctx.set_lifetime_elision(LifetimeElisionKind::Elided(Region::new_static(ctx.interner))); let result = StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store()); @@ -1628,6 +1738,7 @@ pub(crate) fn type_for_type_alias_with_diagnostics( t.into(), &generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); let res = StoredEarlyBinder::bind( @@ -1674,6 +1785,7 @@ pub(crate) fn impl_self_ty_with_diagnostics( impl_id.into(), &generics, LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + LifetimeLoweringMode::Bound, ); let ty = ctx.lower_ty(impl_data.self_ty); assert!(!ty.has_escaping_bound_vars()); @@ -1722,6 +1834,7 @@ pub(crate) fn const_param_types_with_diagnostics( def, &generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ); ctx.forbid_params_after(0, ForbidParamsAfterReason::ConstParamTy); for (local_id, param_data) in data.iter_type_or_consts() { @@ -1793,6 +1906,7 @@ pub(crate) fn field_types_with_diagnostics( generic_def, &generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ); for (field_id, field_data) in var_data.fields().iter() { let ty = ctx.lower_ty(field_data.type_ref); @@ -1933,6 +2047,7 @@ fn resolve_type_param_assoc_type_shorthand( def, generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ); let interner = ctx.interner; let generics = generics.get().unwrap(); @@ -2113,6 +2228,7 @@ pub(crate) fn type_alias_bounds_with_diagnostics( type_alias.into(), &generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ); let interner = ctx.interner; @@ -2351,6 +2467,7 @@ fn generic_predicates( def, generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ); let generics = generics.get().unwrap(); let sized_trait = ctx.lang_items.Sized; @@ -2568,6 +2685,7 @@ pub(crate) fn generic_defaults_with_diagnostics( def, generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ) .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed); let generics = generics.get().unwrap(); @@ -2657,6 +2775,7 @@ fn fn_sig_for_fn( def.into(), &generics, LifetimeElisionKind::for_fn_params(data), + LifetimeLoweringMode::Bound, ); let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); @@ -2668,6 +2787,7 @@ fn fn_sig_for_fn( def.into(), &generics, LifetimeElisionKind::for_fn_ret(interner), + LifetimeLoweringMode::Bound, ) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); let ret = match data.ret_type { @@ -2676,19 +2796,21 @@ fn fn_sig_for_fn( }; let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); - ctx_params.diagnostics.extend(ctx_ret.diagnostics); ctx_params.defined_anon_consts.extend(ctx_ret.defined_anon_consts); - // If/when we track late bound vars, we need to switch this to not be `dummy` - let result = StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - inputs_and_output, - fn_sig_kind: FnSigKind::new( - data.abi, - if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, - data.is_varargs(), - ), - }))); + let binder = TyLoweringContext::bound_vars(db, interner, def.into(), &generics); + let result = StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::bind_with_vars( + FnSig { + inputs_and_output, + fn_sig_kind: FnSigKind::new( + data.abi, + if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, + data.is_varargs(), + ), + }, + binder, + ))); TyLoweringResult::from_ctx(result, ctx_params) } @@ -2749,6 +2871,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( type_alias.into(), &generics, LifetimeElisionKind::AnonymousReportError, + LifetimeLoweringMode::Bound, ); // FIXME: we should never create non-existential predicates in the first place // For now, use an error type so we don't run into dummy binder issues diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 6633215679873..27d52881c6471 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -956,16 +956,20 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque, ) => { let ty = this.ctx.lower_ty(type_ref); + let bound_vars = this.ctx.peek_bound_vars(); let pred = Clause(Predicate::new( interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Projection( - ProjectionPredicate { - projection_term, - term: ty.into(), - }, + Binder::bind_with_vars( + rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Projection( + ProjectionPredicate { + projection_term, + term: ty.into(), + }, + ), ), - )), + bound_vars, + ), )); predicates.push((pred, GenericPredicateSource::SelfOnly)); } @@ -1187,7 +1191,7 @@ pub(crate) fn substs_from_args_and_bindings<'db>( ctx, ); - let mut substs = Vec::with_capacity(def_generics.len()); + let mut substs = Vec::with_capacity(def_generics.len(true)); substs.extend( def_generics.iter_parent_id().enumerate().map(|(idx, id)| ctx.parent_arg(idx as u32, id)), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 2772663ec9ff2..c5868ab6b5271 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -1,7 +1,10 @@ //! This module is concerned with finding methods that a given type provides. //! For details about how this works in rustc, see the method lookup page in the -//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) -//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. +//! [rustc guide] and the corresponding code mostly in +//! [`rustc_hir_typeck/method/probe.rs`]. +//! +//! [rustc guide]: https://rust-lang.github.io/rustc-guide/method-lookup.html +//! [`rustc_hir_typeck/method/probe.rs`]: https://github.com/rust-lang/rust/blob/5503df87342a73d0c29126a7e08dc9c1255c46ad/compiler/rustc_hir_typeck/src/method/probe.rs mod confirm; mod probe; @@ -13,7 +16,7 @@ use tracing::{debug, instrument}; use base_db::Crate; use hir_def::{ - AssocItemId, BlockId, BuiltinDeriveImplId, ConstId, FunctionId, GenericParamId, HasModule, + AssocItemId, BlockIdLt, BuiltinDeriveImplId, ConstId, FunctionId, GenericParamId, HasModule, ImplId, ItemContainerId, ModuleId, TraitId, attrs::AttrFlags, builtin_derive::BuiltinDeriveImplMethod, @@ -233,7 +236,7 @@ impl<'db> InferenceTable<'db> { let args = GenericArgs::for_item( self.interner(), trait_def_id.into(), - |param_idx, param_id, _| match param_id { + |param_idx, param_id, _, _| match param_id { GenericParamId::LifetimeParamId(_) | GenericParamId::ConstParamId(_) => { unreachable!("did not expect operator trait to have lifetime/const") } @@ -559,9 +562,9 @@ pub struct InherentImpls { } #[salsa::tracked] -impl InherentImpls { +impl<'db> InherentImpls { #[salsa::tracked(returns(ref))] - pub fn for_crate(db: &dyn HirDatabase, krate: Crate) -> Self { + pub fn for_crate(db: &'db dyn HirDatabase, krate: Crate) -> Self { let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered(); let crate_def_map = crate_def_map(db, krate); @@ -570,7 +573,7 @@ impl InherentImpls { } #[salsa::tracked(returns(ref))] - pub fn for_block(db: &dyn HirDatabase, block: BlockId) -> Option> { + pub fn for_block(db: &'db dyn HirDatabase, block: BlockIdLt<'db>) -> Option> { let _p = tracing::info_span!("inherent_impls_in_block_query").entered(); let block_def_map = block_def_map(db, block); @@ -623,13 +626,13 @@ impl InherentImpls { self.map.get(self_ty).map(|it| &**it).unwrap_or_default() } - pub fn for_each_crate_and_block( - db: &dyn HirDatabase, + pub fn for_each_crate_and_block<'db>( + db: &'db dyn HirDatabase, krate: Crate, - block: Option, + block: Option>, for_each: &mut dyn FnMut(&InherentImpls), ) { - let blocks = std::iter::successors(block, |block| block.loc(db).module.block(db)); + let blocks = std::iter::successors(block, |block| block.module(db).block(db)); blocks.filter_map(|block| Self::for_block(db, block).as_deref()).for_each(&mut *for_each); for_each(Self::for_crate(db, krate)); } @@ -668,9 +671,9 @@ pub struct TraitImpls { } #[salsa::tracked] -impl TraitImpls { +impl<'db> TraitImpls { #[salsa::tracked(returns(ref))] - pub fn for_crate(db: &dyn HirDatabase, krate: Crate) -> Arc { + pub fn for_crate(db: &'db dyn HirDatabase, krate: Crate) -> Arc { let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered(); let crate_def_map = crate_def_map(db, krate); @@ -679,7 +682,7 @@ impl TraitImpls { } #[salsa::tracked(returns(as_deref))] - pub fn for_block(db: &dyn HirDatabase, block: BlockId) -> Option> { + pub fn for_block(db: &'db dyn HirDatabase, block: BlockIdLt<'db>) -> Option> { let _p = tracing::info_span!("inherent_impls_in_block_query").entered(); let block_def_map = block_def_map(db, block); @@ -688,7 +691,7 @@ impl TraitImpls { } #[salsa::tracked(returns(deref))] - pub fn for_crate_and_deps(db: &dyn HirDatabase, krate: Crate) -> Box<[Arc]> { + pub fn for_crate_and_deps(db: &'db dyn HirDatabase, krate: Crate) -> Box<[Arc]> { krate.transitive_deps(db).iter().map(|&dep| Self::for_crate(db, dep).clone()).collect() } } @@ -823,23 +826,23 @@ impl TraitImpls { } } - pub fn for_each_crate_and_block( - db: &dyn HirDatabase, + pub fn for_each_crate_and_block<'db>( + db: &'db dyn HirDatabase, krate: Crate, - block: Option, + block: Option>, for_each: &mut dyn FnMut(&TraitImpls), ) { - let blocks = std::iter::successors(block, |block| block.loc(db).module.block(db)); + let blocks = std::iter::successors(block, |block| block.module(db).block(db)); blocks.filter_map(|block| Self::for_block(db, block)).for_each(&mut *for_each); Self::for_crate_and_deps(db, krate).iter().map(|it| &**it).for_each(for_each); } /// Like [`Self::for_each_crate_and_block()`], but takes in account two blocks, one for a trait and one for a self type. - pub fn for_each_crate_and_block_trait_and_type( - db: &dyn HirDatabase, + pub fn for_each_crate_and_block_trait_and_type<'db>( + db: &'db dyn HirDatabase, krate: Crate, - type_block: Option, - trait_block: Option, + type_block: Option>, + trait_block: Option>, for_each: &mut dyn FnMut(&TraitImpls), ) { let in_self_and_deps = TraitImpls::for_crate_and_deps(db, krate); @@ -850,10 +853,11 @@ impl TraitImpls { // that means there can't be duplicate impls; if they meet, we stop the search of the deeper block. // This breaks when they are equal (both will stop immediately), therefore we handle this case // specifically. - let blocks_iter = |block: Option| { - std::iter::successors(block, |block| block.loc(db).module.block(db)) + let blocks_iter = |block: Option>| { + std::iter::successors(block, |block| block.module(db).block(db)) }; - let for_each_block = |current_block: Option, other_block: Option| { + let for_each_block = |current_block: Option>, + other_block: Option>| { blocks_iter(current_block) .take_while(move |&block| { other_block.is_none_or(|other_block| other_block != block) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 796a37137e0de..3be9afdf45437 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -2046,7 +2046,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { let args = GenericArgs::for_item( self.interner(), method.into(), - |param_index, param_id, _| { + |param_index, param_id, _, _| { let i = param_index as usize; if i < args.len() { args[i] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index d004e3b5daef6..2fa3f5e797b5e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -37,7 +37,7 @@ mod lower; mod monomorphization; mod pretty; -pub use borrowck::{BorrowckResult, MutabilityReason, borrowck_query}; +pub use borrowck::{BorrowckResult, MutabilityReason}; pub use eval::{ Evaluator, MirEvalError, VTableMap, interpret_mir, pad16, render_const_using_debug_impl, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index c5367f630e1ad..a9271675a0745 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -133,59 +133,66 @@ fn all_mir_bodies<'db>( } } -#[salsa_macros::tracked(returns(ref), lru = 2024)] -pub fn borrowck_query( - db: &dyn HirDatabase, - def: InferBodyId, -) -> Result, MirLowerError> { - let _p = tracing::info_span!("borrowck_query").entered(); - let module = def.module(db); - let interner = DbInterner::new_with(db, module.krate(db)); - let env = db.trait_environment(def.generic_def(db)); - // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`. - let typing_mode = TypingMode::borrowck(interner, def.into()); - let res = all_mir_bodies( - db, - def, - |body, owner| { - // FIXME(next-solver): Opaques. - let infcx = interner.infer_ctxt().build(typing_mode); - BorrowckResult { - owner, - mutability_of_locals: mutability_of_locals(&infcx, env, body), - moved_out_of_ref: moved_out_of_ref(&infcx, env, body), - partially_moved: partially_moved(&infcx, env, body), - borrow_regions: borrow_regions(db, body), - } - }, - |(parent, parent_mir_body), (child, child_mir_body)| { - for (upvar, child_locals) in &child_mir_body.upvar_locals { - let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else { - continue; - }; - for (child_local, capture_place) in child_locals { - if !capture_place - .projections - .iter() - .any(|proj| matches!(proj.kind, HirProjectionKind::Deref)) - { - let parent_mol = &mut parent.mutability_of_locals[parent_local]; - match (&*parent_mol, &child.mutability_of_locals[*child_local]) { - (MutabilityReason::Mut { .. }, _) => {} - (_, MutabilityReason::Mut { .. }) => { - // FIXME: Fix the child spans. - *parent_mol = MutabilityReason::Mut { spans: Vec::new() } +impl InferBodyId { + pub fn borrowck(self, db: &dyn HirDatabase) -> Result<&[BorrowckResult], MirLowerError> { + return borrowck_query(db, self).map_err(|e| e.clone()); + + #[salsa::tracked(returns(as_deref), lru = 2024)] + fn borrowck_query( + db: &dyn HirDatabase, + def: InferBodyId, + ) -> Result, MirLowerError> { + let _p = tracing::info_span!("InferBodyId::borrowck").entered(); + let module = def.module(db); + let interner = DbInterner::new_with(db, module.krate(db)); + let env = db.trait_environment(def.generic_def(db)); + // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`. + let typing_mode = TypingMode::borrowck(interner, def.into()); + all_mir_bodies( + db, + def, + |body, owner| { + // FIXME(next-solver): Opaques. + let infcx = interner.infer_ctxt().build(typing_mode); + BorrowckResult { + owner, + mutability_of_locals: mutability_of_locals(&infcx, env, body), + moved_out_of_ref: moved_out_of_ref(&infcx, env, body), + partially_moved: partially_moved(&infcx, env, body), + borrow_regions: borrow_regions(db, body), + } + }, + |(parent, parent_mir_body), (child, child_mir_body)| { + for (upvar, child_locals) in &child_mir_body.upvar_locals { + let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else { + continue; + }; + for (child_local, capture_place) in child_locals { + if !capture_place + .projections + .iter() + .any(|proj| matches!(proj.kind, HirProjectionKind::Deref)) + { + let parent_mol = &mut parent.mutability_of_locals[parent_local]; + match (&*parent_mol, &child.mutability_of_locals[*child_local]) { + (MutabilityReason::Mut { .. }, _) => {} + (_, MutabilityReason::Mut { .. }) => { + // FIXME: Fix the child spans. + *parent_mol = MutabilityReason::Mut { spans: Vec::new() } + } + (MutabilityReason::Not, _) => {} + (_, MutabilityReason::Not) => { + *parent_mol = MutabilityReason::Not + } + (MutabilityReason::Unused, MutabilityReason::Unused) => {} + } } - (MutabilityReason::Not, _) => {} - (_, MutabilityReason::Not) => *parent_mol = MutabilityReason::Not, - (MutabilityReason::Unused, MutabilityReason::Unused) => {} } } - } - } - }, - )?; - Ok(res) + }, + ) + } + } } fn moved_out_of_ref<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 855dafe2d9bac..b968f33e81d0d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -738,6 +738,42 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { self.cached_ptr_size } + fn caller_location_fields(&self, owner: InferBodyId, span: MirSpan) -> (String, u32, u32) { + let Some((file_id, text_range)) = self.resolve_mir_span(owner, span) else { + return (String::new(), 0, 0); + }; + let source_root = self.db.file_source_root(file_id).source_root_id(self.db); + let source_root = self.db.source_root(source_root).source_root(self.db); + let path = source_root.path_for_file(&file_id).map(|path| path.to_string()); + let (line, col) = self.db.line_column(file_id, text_range.start()).unwrap_or((0, 0)); + (path.unwrap_or_default(), line + 1, col + 1) + } + + fn resolve_mir_span(&self, owner: InferBodyId, span: MirSpan) -> Option<(FileId, TextRange)> { + let (source_map, self_param_syntax) = match owner { + InferBodyId::DefWithBodyId(def) => { + let body = &Body::with_source_map(self.db, def).1; + (&**body, body.self_param_syntax()) + } + InferBodyId::AnonConstId(def) => { + (ExpressionStore::with_source_map(self.db, def.loc(self.db).owner).1, None) + } + }; + let span: InFile = match span { + MirSpan::ExprId(e) => source_map.expr_syntax(e).ok()?.map(|it| it.into()), + MirSpan::PatId(p) => source_map.pat_syntax(p).ok()?.map(|it| it.syntax_node_ptr()), + MirSpan::BindingId(b) => source_map + .patterns_for_binding(b) + .iter() + .find_map(|p| source_map.pat_syntax(*p).ok())? + .map(|it| it.syntax_node_ptr()), + MirSpan::SelfParam => self_param_syntax?.map(|it| it.syntax_node_ptr()), + MirSpan::Unknown => return None, + }; + let file_id = span.file_id.original_file(self.db); + Some((file_id.file_id(self.db), span.value.text_range())) + } + fn projected_ty(&self, ty: PlaceTy<'db>, proj: PlaceElem) -> PlaceTy<'db> { let pair = (ty, proj); if let Some(r) = self.projected_ty_cache.borrow().get(&pair) { @@ -3181,26 +3217,33 @@ pub fn render_const_using_debug_impl<'db>( let Some(debug_fmt_fn) = lang_items.Debug_fmt else { not_supported!("core::fmt::Debug::fmt not found"); }; - // a1 = &[""] - let a1 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size())?; - // a2 = &[::core::fmt::ArgumentV1::new(&(THE_CONST), ::core::fmt::Debug::fmt)] - // FIXME: we should call the said function, but since its name is going to break in the next rustc version - // and its ABI doesn't break yet, we put it in memory manually. - let a2 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size())?; - evaluator.write_memory(a2, &data.addr.to_bytes())?; + let ptr_size = evaluator.ptr_size(); + // Construct the arguments of `format_args!("{:?}", THE_CONST)` directly in memory and hand + // them to `std::fmt::format`. + // + // `core::fmt::rt::Argument` is a niche-encoded `Placeholder { value: NonNull<()>, formatter }`, + // i.e. two words: a pointer to the value, and the type-erased `::fmt` function. + // A non-null `value` is what distinguishes the `Placeholder` variant from `Count`. + let argument = evaluator.heap_allocate(ptr_size * 2, ptr_size)?; + evaluator.write_memory(argument, &data.addr.to_bytes())?; let debug_fmt_fn_ptr = evaluator.vtable_map.id(Ty::new_fn_def( evaluator.interner(), CallableDefId::FunctionId(debug_fmt_fn).into(), GenericArgs::new_from_slice(&[ty.into()]), )); - evaluator.write_memory(a2.offset(evaluator.ptr_size()), &debug_fmt_fn_ptr.to_le_bytes())?; - // a3 = ::core::fmt::Arguments::new_v1(a1, a2) - // FIXME: similarly, we should call function here, not directly working with memory. - let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size())?; - evaluator.write_memory(a3, &a1.to_bytes())?; - evaluator.write_memory(a3.offset(evaluator.ptr_size()), &[1])?; - evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a2.to_bytes())?; - evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?; + evaluator.write_memory(argument.offset(ptr_size), &debug_fmt_fn_ptr.to_le_bytes())?; + // Since Rust 1.93 `core::fmt::Arguments` is two words wide: + // struct Arguments<'a> { template: NonNull, args: NonNull> } + // `template` points at a byte-encoded format string; `format_args!("{:?}", x)` encodes to a + // single default placeholder (`0xC0`) followed by the end marker (`0x00`). `args` points at + // our one-element argument array, and must stay pointer-aligned: `core` uses the low bit of + // `args` as a tag (1 = inline `&str` form, 0 = placeholder form), and heap allocations here + // are pointer-aligned so the bit is 0 as required. + let template = evaluator.heap_allocate(2, 1)?; + evaluator.write_memory(template, &[0xC0, 0x00])?; + let arguments = evaluator.heap_allocate(ptr_size * 2, ptr_size)?; + evaluator.write_memory(arguments, &template.to_bytes())?; + evaluator.write_memory(arguments.offset(ptr_size), &argument.to_bytes())?; let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully( db, &hir_def::expr_store::path::Path::from_known_path_with_no_generic(path![std::fmt::format]), @@ -3210,13 +3253,24 @@ pub fn render_const_using_debug_impl<'db>( }; let interval = evaluator.interpret_mir( db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, - [IntervalOrOwned::Borrowed(Interval { addr: a3, size: evaluator.ptr_size() * 6 })] - .into_iter(), + [IntervalOrOwned::Borrowed(Interval { addr: arguments, size: ptr_size * 2 })].into_iter(), )?; let message_string = interval.get(&evaluator)?; - let addr = - Address::from_bytes(&message_string[evaluator.ptr_size()..2 * evaluator.ptr_size()])?; - let size = from_bytes!(usize, message_string[2 * evaluator.ptr_size()..]); + let words = [ + from_bytes!(usize, message_string[0..ptr_size]), + from_bytes!(usize, message_string[ptr_size..2 * ptr_size]), + from_bytes!(usize, message_string[2 * ptr_size..3 * ptr_size]), + ]; + let Some(addr) = words.into_iter().map(Address::from_usize).find(|it| matches!(it, Heap(_))) + else { + // No heap buffer means the formatted string is empty. + return Ok(String::new()); + }; + let size = words + .into_iter() + .filter(|&it| !matches!(Address::from_usize(it), Heap(_))) + .min() + .unwrap_or(0); Ok(std::string::String::from_utf8_lossy(evaluator.read_memory(addr, size)?).into_owned()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 7a6cbb8ca3739..d2a74f20a5626 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1062,12 +1062,15 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let ans = ptr + offset * size; destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) } - "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" | "assume" => { + "assert_inhabited" + | "assert_zero_valid" + | "assert_uninit_valid" + | "assert_mem_uninitialized_valid" => { // FIXME: We should actually implement these checks Ok(()) } "forget" => { - // We don't call any drop glue yet, so there is nothing here + // FIXME Ok(()) } "transmute" | "transmute_unchecked" => { @@ -1334,6 +1337,84 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { .write_from_interval(self, meta.interval)?; Ok(()) } + "fabs" => { + let [arg] = args else { + return Err(MirEvalError::InternalError( + "fabs intrinsic signature doesn't match fn (T) -> T".into(), + )); + }; + let mut bytes = arg.get(self)?.to_vec(); + if let Some(sign_byte) = bytes.last_mut() { + *sign_byte &= 0x7f; + } + destination.write_from_bytes(self, &bytes) + } + "unreachable" => { + return Err(MirEvalError::UndefinedBehavior( + "`unreachable` intrinsic executed".to_owned(), + )); + } + "const_allocate" => { + let [size, align] = args else { + return Err(MirEvalError::InternalError( + "const_allocate args are not provided".into(), + )); + }; + let size = from_bytes!(usize, size.get(self)?); + let align = from_bytes!(usize, align.get(self)?); + let result = self.heap_allocate(size, align)?; + destination.write_from_bytes(self, &result.to_bytes()) + } + "const_deallocate" => Ok(()), + "caller_location" => { + let Some(location_adt) = self.lang_items().PanicLocation else { + not_supported!("`caller_location` requires the `panic_location` lang item"); + }; + let location_ty = self.db.ty(location_adt.into()).skip_binder(); + let TyKind::Adt(_, subst) = location_ty.kind() else { + return Err(MirEvalError::InternalError( + "`panic_location` lang item is not an ADT".into(), + )); + }; + let layout = self.layout(location_ty)?; + let (file, line, col) = self.caller_location_fields(locals.body.owner, span); + let file_len = file.len(); + let file_addr = self.heap_allocate(file_len + 1, 1)?; + self.write_memory(file_addr, file.as_bytes())?; + let ptr_size = self.ptr_size(); + let field_types = self.db.field_types(location_adt.into()); + let mut line_col = [line, col].into_iter(); + let mut fields = Vec::with_capacity(field_types.iter().count()); + for (_, field) in field_types.iter() { + let field_ty = field.ty().instantiate(self.interner(), subst).skip_norm_wip(); + let bytes = + if matches!(field_ty.kind(), TyKind::Uint(rustc_type_ir::UintTy::U32)) { + line_col.next().unwrap_or(0).to_le_bytes().to_vec() + } else { + let size = + self.size_of_sized(field_ty, locals, "caller_location field")?; + if size == ptr_size * 2 { + // The string slice pointing at the file name: (data pointer, length). + let mut bytes = file_addr.to_bytes()[..ptr_size].to_vec(); + bytes.extend_from_slice(&file_len.to_le_bytes()[..ptr_size]); + bytes + } else { + vec![0; size] + } + }; + fields.push(IntervalOrOwned::Owned(bytes)); + } + let location = self.construct_with_layout( + layout.size.bytes_usize(), + &layout, + None, + fields.into_iter(), + )?; + let location_addr = + self.heap_allocate(layout.size.bytes_usize(), layout.align.bytes() as usize)?; + self.write_memory(location_addr, &location)?; + destination.write_from_bytes(self, &location_addr.to_bytes()[..ptr_size]) + } _ if needs_override => not_supported!("intrinsic {name} is not implemented"), _ => return Ok(false), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index ccc4815d6cbcc..622519445c69b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -1084,3 +1084,72 @@ fn main() { "#, ); } + +#[test] +fn fabs_intrinsic() { + check_pass( + r#" +//- minicore: copy, panic +pub unsafe trait FloatPrimitive: Sized + Copy {} +unsafe impl FloatPrimitive for f32 {} +unsafe impl FloatPrimitive for f64 {} + +#[rustc_intrinsic] +fn fabs(x: T) -> T; + +fn should_not_reach() { panic!() } + +fn main() { + if fabs(-3.5f32) != 3.5f32 { + should_not_reach(); + } + if fabs(3.5f32) != 3.5f32 { + should_not_reach(); + } + if fabs(-3.5f64) != 3.5f64 { + should_not_reach(); + } +} +"#, + ); +} + +#[test] +fn unreachable_intrinsic() { + check_error_with( + r#" +#[rustc_intrinsic] +fn unreachable() -> !; + +fn main() { + unreachable(); +} +"#, + |e| { + let mut err = &e; + while let MirEvalError::InFunction(inner, _) = err { + err = inner; + } + matches!(err, MirEvalError::UndefinedBehavior(_)) + }, + ); +} + +#[test] +fn caller_location_intrinsic() { + check_pass( + r#" +//- minicore: panic_location +fn should_not_reach() { + panic!() +} + +fn main() { + let loc = core::panic::Location::caller(); + if loc.line() != 1 || loc.column() != 1 { + should_not_reach(); + } +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index ab7e6df3f5156..8cc59ecd0c1eb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -961,18 +961,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } Expr::Await { .. } => not_supported!("await"), Expr::Yeet { .. } => not_supported!("yeet"), - &Expr::Const(_) => { - // let subst = self.placeholder_subst(); - // self.lower_const( - // id.into(), - // current, - // place, - // subst, - // expr_id.into(), - // self.expr_ty_without_adjust(expr_id), - // )?; - // Ok(Some(current)) - not_supported!("const block") + &Expr::Const(id) => { + // Inline const blocks (`const { .. }`) are stored with their inner expression in + // the same body (see inference, which infers the inner expression directly), so we + // lower that expression in place. Const-ness is irrelevant here: MIR evaluation + // already runs in a const context. + self.lower_expr_to_place(id, place, current) } Expr::Cast { expr, type_ref: _ } => { let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else { @@ -2129,7 +2123,7 @@ fn cast_kind<'db>( }) } -#[salsa_macros::tracked(returns(ref), cycle_result = mir_body_for_closure_cycle_result)] +#[salsa_macros::tracked(returns(as_ref), cycle_result = mir_body_for_closure_cycle_result)] pub fn mir_body_for_closure_query<'db>( db: &'db dyn HirDatabase, closure: InternedClosureId, @@ -2288,7 +2282,7 @@ pub fn mir_body_for_closure_query<'db>( Ok(ctx.result) } -#[salsa_macros::tracked(returns(ref), cycle_result = mir_body_cycle_result)] +#[salsa_macros::tracked(returns(as_ref), cycle_result = mir_body_cycle_result)] pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: InferBodyId) -> Result<'db, MirBody> { let krate = def.krate(db); let edition = krate.data(db).edition; @@ -2358,8 +2352,14 @@ pub fn lower_body_to_mir<'db>( ) -> Result<'db, MirBody> { // Extract params and self_param only when lowering the body's root expression for a function. if let Some(fid) = owner.as_function() { - let callable_sig = - db.callable_item_signature(fid.into()).instantiate_identity().skip_binder(); + let callable_sig = { + let resolver = owner.resolver(db); + let interner = DbInterner::new_with(db, resolver.krate()); + interner.liberate_late_bound_regions( + fid.into(), + db.callable_item_signature(fid.into()).instantiate_identity().skip_norm_wip(), + ) + }; let mut param_tys = callable_sig.inputs().iter().copied(); let self_param = self_param.and_then(|id| Some((id, param_tys.next()?))); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 66b51a0e95b51..bd1ad70fe6a14 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -602,6 +602,14 @@ impl<'db> MirLowerCtx<'_, 'db> { shape: AdtPatternShape<'_>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { + let place_ty = cond_place.ty(&self.result, &self.infcx, self.env).ty; + let Some((place_adt, _)) = place_ty.as_adt() else { + return Err(MirLowerError::TypeError("non ADT type matched with ADT pattern")); + }; + if place_adt != variant.adt_id(self.db) { + return Err(MirLowerError::TypeError("ADT pattern does not match place type")); + } + Ok(match variant { VariantId::EnumVariantId(v) => { if mode == MatchingMode::Check { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs index d8f7d549d6bff..d42072afa4b7c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -1,7 +1,7 @@ use hir_def::DefWithBodyId; use test_fixture::WithFixture; -use crate::{db::HirDatabase, setup_tracing, test_db::TestDB}; +use crate::{InferBodyId, db::HirDatabase, setup_tracing, test_db::TestDB}; fn lower_mir(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let _tracing = setup_tracing(); @@ -78,7 +78,7 @@ fn check_borrowck(#[rust_analyzer::rust_fixture] ra_fixture: &str) { } for body in bodies { - let _ = db.borrowck(body.into()); + let _ = InferBodyId::from(body).borrowck(&db); } }) } @@ -134,3 +134,19 @@ fn alias(x: T::A) { "#, ); } + +#[test] +fn borrowck_opaque_downcast_recovery_does_not_panic() { + check_borrowck( + r#" +//- minicore: option, sized +struct PathBuf; +fn opaque(path: T) -> impl Sized { + Some(path) +} +fn caller(path: &PathBuf) { + let Some(value) = opaque(path) else { return }; +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index 06871a3f18424..bcc86ba4bfd79 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -238,7 +238,7 @@ impl<'db> Filler<'db> { } } -#[salsa_macros::tracked(returns(ref), cycle_result = monomorphized_mir_body_cycle_result)] +#[salsa_macros::tracked(returns(as_ref), cycle_result = monomorphized_mir_body_cycle_result)] pub fn monomorphized_mir_body_query( db: &dyn HirDatabase, owner: InferBodyId, @@ -262,7 +262,7 @@ fn monomorphized_mir_body_cycle_result( Err(MirLowerError::Loop) } -#[salsa_macros::tracked(returns(ref), cycle_result = monomorphized_mir_body_for_closure_cycle_result)] +#[salsa_macros::tracked(returns(as_ref), cycle_result = monomorphized_mir_body_for_closure_cycle_result)] pub fn monomorphized_mir_body_for_closure_query( db: &dyn HirDatabase, closure: InternedClosureId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs index af823aa005d08..0a41874374c21 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs @@ -8,7 +8,8 @@ use rustc_type_ir::{ use crate::next_solver::{BoundConst, FxIndexMap}; use super::{ - Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind, + Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, SolverDefId, Ty, + TyKind, }; /// A delegate used when instantiating bound vars. @@ -219,4 +220,19 @@ impl<'db> DbInterner<'db> { { self.instantiate_bound_regions(value, |_| Region::new_erased(self)).0 } + + /// Replaces any late-bound regions bound in `value` with + /// free variants attached to `all_outlive_scope`. + pub fn liberate_late_bound_regions( + self, + all_outlive_scope: SolverDefId, + value: Binder<'db, T>, + ) -> T + where + T: TypeFoldable>, + { + self.instantiate_bound_regions_uncached(value, |br| { + Region::new_late_param(self, all_outlive_scope, br) + }) + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 51f070cd64e8c..4e5f3c8c49c13 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -9,7 +9,7 @@ use std::{hint::unreachable_unchecked, marker::PhantomData, ptr::NonNull}; use arrayvec::ArrayVec; -use hir_def::{GenericDefId, GenericParamId}; +use hir_def::{GenericDefId, GenericParamId, hir::generics::LifetimeParamData}; use intern::InternedRef; use rustc_type_ir::{ ClosureArgs, ConstVid, CoroutineArgs, CoroutineClosureArgs, FallibleTypeFolder, @@ -518,10 +518,15 @@ impl<'db> GenericArgs<'db> { defs: &Generics<'db>, mut mk_kind: F, ) where - F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut( + u32, + GenericParamId, + Option<&LifetimeParamData>, + &[GenericArg<'db>], + ) -> GenericArg<'db>, { - defs.iter_id().enumerate().for_each(|(idx, param_id)| { - let new_arg = mk_kind(idx as u32, param_id, args.as_ref()); + defs.iter().enumerate().for_each(|(idx, (param, lt_data))| { + let new_arg = mk_kind(idx as u32, param, lt_data, args.as_ref()); args.push(new_arg); }); } @@ -529,7 +534,12 @@ impl<'db> GenericArgs<'db> { #[cold] fn fill_vec_builder(defs: &Generics<'db>, count: usize, mk_kind: F) -> GenericArgs<'db> where - F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut( + u32, + GenericParamId, + Option<&LifetimeParamData>, + &[GenericArg<'db>], + ) -> GenericArg<'db>, { let mut args = Vec::with_capacity(count); Self::fill_builder(&mut args, defs, mk_kind); @@ -547,7 +557,12 @@ impl<'db> GenericArgs<'db> { mk_kind: F, ) -> GenericArgs<'db> where - F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut( + u32, + GenericParamId, + Option<&LifetimeParamData>, + &[GenericArg<'db>], + ) -> GenericArg<'db>, { let defs = interner.generics_of(def_id); let count = defs.count(); @@ -565,7 +580,9 @@ impl<'db> GenericArgs<'db> { /// Creates an all-error `GenericArgs`. pub fn error_for_item(interner: DbInterner<'db>, def_id: SolverDefId) -> GenericArgs<'db> { - GenericArgs::for_item(interner, def_id, |_, id, _| GenericArg::error_from_id(interner, id)) + GenericArgs::for_item(interner, def_id, |_, id, _, _| { + GenericArg::error_from_id(interner, id) + }) } /// Like `for_item`, but prefers the default of a parameter if it has any. @@ -578,9 +595,11 @@ impl<'db> GenericArgs<'db> { F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let defaults = interner.db.generic_defaults(def_id); - Self::for_item(interner, def_id.into(), |idx, id, prev| match defaults.get(idx as usize) { - Some(default) => default.instantiate(interner, prev).skip_norm_wip(), - None => fallback(idx, id, prev), + Self::for_item(interner, def_id.into(), |idx, id, _, prev| { + match defaults.get(idx as usize) { + Some(default) => default.instantiate(interner, prev).skip_norm_wip(), + None => fallback(idx, id, prev), + } }) } @@ -595,7 +614,7 @@ impl<'db> GenericArgs<'db> { F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let mut iter = first.into_iter(); - Self::for_item(interner, def_id, |idx, id, prev| { + Self::for_item(interner, def_id, |idx, id, _, prev| { iter.next().unwrap_or_else(|| fallback(idx, id, prev)) }) } @@ -676,7 +695,7 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< interner: DbInterner<'db>, def_id: as rustc_type_ir::Interner>::DefId, ) -> as rustc_type_ir::Interner>::GenericArgs { - Self::for_item(interner, def_id, |index, kind, _| mk_param(interner, index, kind)) + Self::for_item(interner, def_id, |index, kind, _, _| mk_param(interner, index, kind)) } fn extend_with_error( @@ -684,7 +703,7 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< def_id: as rustc_type_ir::Interner>::DefId, original_args: &[ as rustc_type_ir::Interner>::GenericArg], ) -> as rustc_type_ir::Interner>::GenericArgs { - Self::for_item(interner, def_id, |index, kind, _| { + Self::for_item(interner, def_id, |index, kind, _, _| { if let Some(arg) = original_args.get(index as usize) { *arg } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs index a798582cb9891..49dbbcb06bcc8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs @@ -1,6 +1,9 @@ //! Things related to generics in the next-trait-solver. -use hir_def::{GenericDefId, GenericParamId}; +use hir_def::{ + GenericDefId, GenericParamId, TypeParamId, + hir::generics::{GenericParamDataRef, LifetimeParamData}, +}; use crate::db::HirDatabase; @@ -10,11 +13,13 @@ use super::DbInterner; pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics<'_> { let db = interner.db; - let def = match (def.try_into(), def) { - (Ok(def), _) => def, + let (def, consider_late_bound) = match (def.try_into(), def) { + (Ok(def), _) => (def, false), (_, SolverDefId::InternedOpaqueTyId(id)) => match id.loc(db) { - crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => function_id.into(), - crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => type_alias_id.into(), + crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => (function_id.into(), true), + crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => { + (type_alias_id.into(), true) + } }, (_, SolverDefId::BuiltinDeriveImplId(id)) => { return crate::builtin_derive::generics_of(interner, id); @@ -23,7 +28,7 @@ pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics<' let loc = id.loc(db); let generic_def = loc.owner.generic_def(db); return if loc.allow_using_generic_params { - Generics::from_generic_def(db, generic_def) + Generics::from_generic_def(db, generic_def, false) } else { #[expect( deprecated, @@ -33,13 +38,14 @@ pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics<' Generics { generics: crate::generics::Generics::empty(generic_def), additional_param: None, + consider_late_bound: false, } }; } _ => panic!("No generics for {def:?}"), }; - Generics::from_generic_def(db, def) + Generics::from_generic_def(db, def, consider_late_bound) } #[derive(Debug)] @@ -47,31 +53,53 @@ pub struct Generics<'db> { generics: crate::generics::Generics<'db>, /// This is used for builtin derives, specifically `CoercePointee`. additional_param: Option, + consider_late_bound: bool, } impl<'db> Generics<'db> { - pub(crate) fn from_generic_def(db: &'db dyn HirDatabase, def: GenericDefId) -> Generics<'db> { - Generics { generics: crate::generics::generics(db, def), additional_param: None } + pub(crate) fn from_generic_def( + db: &'db dyn HirDatabase, + def: GenericDefId, + consider_late_bound: bool, + ) -> Generics<'db> { + Generics { + generics: crate::generics::generics(db, def), + additional_param: None, + consider_late_bound, + } } pub(crate) fn from_generic_def_plus_one( db: &'db dyn HirDatabase, def: GenericDefId, - additional_param: GenericParamId, + additional_param: TypeParamId, + consider_late_bound: bool, ) -> Generics<'db> { Generics { generics: crate::generics::generics(db, def), - additional_param: Some(additional_param), + additional_param: Some(additional_param.into()), + consider_late_bound, } } - pub(super) fn iter_id(&self) -> impl Iterator { - self.generics.iter_id().chain(self.additional_param) + pub(super) fn iter( + &self, + ) -> impl Iterator)> { + self.generics + .iter(self.consider_late_bound) + .map(|(id, data)| { + if let GenericParamDataRef::LifetimeParamData(lt_param) = data { + (id, Some(lt_param)) + } else { + (id, None) + } + }) + .chain(self.additional_param.zip(None)) } } impl<'db> rustc_type_ir::inherent::GenericsOf> for Generics<'db> { fn count(&self) -> usize { - self.generics.len() + usize::from(self.additional_param.is_some()) + self.generics.len(self.consider_late_bound) + usize::from(self.additional_param.is_some()) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 2c2f7dbf67301..3fdf0480ebc2d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -840,7 +840,9 @@ impl<'db> InferCtxt<'db> { /// Given a set of generics defined on a type or impl, returns the generic parameters mapping /// each type/region parameter to a fresh inference variable. pub fn fresh_args_for_item(&self, span: Span, def_id: SolverDefId) -> GenericArgs<'db> { - GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind, span)) + GenericArgs::for_item(self.interner, def_id, |_index, kind, _, _| { + self.var_for_def(kind, span) + }) } /// Like [`Self::fresh_args_for_item`], but first uses the args from `first`. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 3c2976eccd980..3e8fab9313185 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -2,6 +2,7 @@ use std::{fmt, ops::ControlFlow}; +use either::Either; use intern::{Interned, InternedRef, InternedSliceRef, impl_internable}; use macros::GenericTypeVisitable; use rustc_abi::ReprOptions; @@ -597,20 +598,17 @@ impl<'db> inherent::AdtDef> for AdtDef { interner: DbInterner<'db>, ) -> EarlyBinder, impl IntoIterator>> { let db = interner.db(); - // FIXME: this is disabled just to match the behavior with chalk right now - let _field_tys = |id: VariantId| { - db.field_types(id).iter().map(|(_, ty)| ty.ty().skip_binder()).collect::>() - }; - let field_tys = |_id: VariantId| vec![]; - let tys: Vec<_> = match self.def_id() { - hir_def::AdtId::StructId(id) => field_tys(id.into()), - hir_def::AdtId::UnionId(id) => field_tys(id.into()), - hir_def::AdtId::EnumId(id) => id - .enum_variants(db) - .variants - .values() - .flat_map(|&(variant_id, _)| field_tys(variant_id.into())) - .collect(), + let field_tys = + |id: VariantId| db.field_types(id).iter().map(|(_, ty)| ty.ty().skip_binder()); + let tys = match self.def_id() { + hir_def::AdtId::StructId(id) => Either::Left(field_tys(id.into())), + hir_def::AdtId::UnionId(id) => Either::Left(field_tys(id.into())), + hir_def::AdtId::EnumId(id) => Either::Right( + id.enum_variants(db) + .variants + .values() + .flat_map(move |&(variant_id, _)| field_tys(variant_id.into())), + ), }; EarlyBinder::bind(tys) @@ -1145,7 +1143,7 @@ impl<'db> Interner for DbInterner<'db> { ) -> (rustc_type_ir::TraitRef, Self::GenericArgsSlice) { let trait_def_id = self.projection_parent(def_id).0; let trait_generics = crate::generics::generics(self.db, trait_def_id.into()); - let trait_generics_len = trait_generics.len(); + let trait_generics_len = trait_generics.len(true); let trait_args = GenericArgs::new_from_slice(&args.as_slice()[..trait_generics_len]); let alias_args = &args.as_slice()[trait_generics_len..]; (TraitRef::new_from_args(self, trait_def_id.into(), trait_args), alias_args) @@ -2486,34 +2484,37 @@ mod tls_cache { db_nonce: Nonce, } + impl Cache { + const fn default() -> Cache { + Cache { + cache: GlobalCache::new(), + revision: Revision::max(), + db_nonce: Nonce::invalid(), + } + } + } + thread_local! { - static GLOBAL_CACHE: RefCell> = const { RefCell::new(None) }; + static GLOBAL_CACHE: RefCell = const { RefCell::new(Cache::default()) }; } pub(super) fn reinit_cache(db: &dyn HirDatabase) { GLOBAL_CACHE.with_borrow_mut(|handle| { let (db_nonce, revision) = db.nonce_and_revision(); - match handle { - Some(handle) => { - if handle.revision != revision || db_nonce != handle.db_nonce { - *handle = Cache { cache: GlobalCache::default(), revision, db_nonce }; - } - } - None => *handle = Some(Cache { cache: GlobalCache::default(), revision, db_nonce }), + if handle.revision != revision || db_nonce != handle.db_nonce { + *handle = Cache { cache: GlobalCache::default(), revision, db_nonce }; } }) } + #[inline] pub(super) fn borrow_assume_valid<'db, T>( db: &'db dyn HirDatabase, f: impl FnOnce(&mut GlobalCache>) -> T, ) -> T { if cfg!(debug_assertions) { - let get_state = || { - GLOBAL_CACHE.with_borrow(|handle| { - handle.as_ref().map(|handle| (handle.db_nonce, handle.revision)) - }) - }; + let get_state = + || GLOBAL_CACHE.with_borrow(|handle| (handle.db_nonce, handle.revision)); let old_state = get_state(); reinit_cache(db); let new_state = get_state(); @@ -2521,7 +2522,6 @@ mod tls_cache { } GLOBAL_CACHE.with_borrow_mut(|handle| { - let handle = handle.as_mut().expect("you assumed the cache is valid!"); // SAFETY: No idea f(unsafe { std::mem::transmute::< @@ -2537,7 +2537,7 @@ mod tls_cache { /// Should be called before getting memory usage estimations, as the solver cache /// is per-revision and usually should be excluded from estimations. pub fn clear_tls_solver_cache() { - GLOBAL_CACHE.with_borrow_mut(|handle| *handle = None); + GLOBAL_CACHE.with_borrow_mut(|handle| *handle = Cache::default()); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index 72a25f4df6da4..dc753a1b47667 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -75,6 +75,15 @@ impl<'db> Region<'db> { Region::new(interner, RegionKind::ReBound(BoundVarIndexKind::Bound(index), bound)) } + pub fn new_late_param( + interner: DbInterner<'db>, + scope: SolverDefId, + bound_region: BoundRegion<'db>, + ) -> Region<'db> { + let late_bound_region = LateParamRegion { scope, bound_region }; + Region::new(interner, RegionKind::ReLateParam(late_bound_region)) + } + pub fn is_placeholder(&self) -> bool { matches!(self.inner(), RegionKind::RePlaceholder(..)) } @@ -155,17 +164,13 @@ pub struct EarlyParamRegion { } #[derive(Copy, Clone, PartialEq, Eq, Hash, GenericTypeVisitable)] -/// The parameter representation of late-bound function parameters, "some region -/// at least as big as the scope `fr.scope`". +/// Represents a liberated late-bound function lifetime parameter. /// -/// Similar to a placeholder region as we create `LateParam` regions when entering a binder -/// except they are always in the root universe and instead of using a boundvar to distinguish -/// between others we use the `DefId` of the parameter. For this reason the `bound_region` field -/// should basically always be `BoundRegionKind::Named` as otherwise there is no way of telling -/// different parameters apart. +/// This denotes some region at least as big as `scope`. It is similar to a placeholder region +/// created when entering a binder, except it always lives in the root universe. pub struct LateParamRegion<'db> { pub scope: SolverDefId, - pub bound_region: BoundRegionKind<'db>, + pub bound_region: BoundRegion<'db>, } impl std::fmt::Debug for LateParamRegion<'_> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index d7036d1056e6a..7bf22b93f7753 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -64,7 +64,7 @@ impl Clone for TestDB { files: self.files.clone(), crates_map: self.crates_map.clone(), events: self.events.clone(), - nonce: Nonce::new(), + nonce: self.nonce, } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index 37da7fc875631..5a4a6562ad3e9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -69,7 +69,7 @@ fn test<'a>( _: &(dyn A + Send), //^ &(dyn A + Send + 'static) _: &'a (dyn Send + A), - //^ &'a (dyn A + Send + 'static) + //^ &(dyn A + Send + 'static) _: &dyn B, //^ &(dyn B + 'static) ) {} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 2ef8999817938..dc882311871fb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2947,3 +2947,49 @@ fn caller() { "#, ); } + +#[test] +fn regression_22772() { + check_no_mismatches( + r#" +trait Resolve { + type Prev; +} + +fn migrations_preserve_index() { + pub struct RefExpr1<'x> { + pub foo: &'x schema::v0::_Ref0, + } + + pub fn new_column<'x, C>() -> &'x C { + loop {} + } + + RefExpr1 { foo: new_column::() }; + + mod schema { + pub struct Foo {} + pub struct FooNew {} + + impl crate::Resolve for FooNew { + type Prev = Foo; + } + + pub mod v0 { + pub type _Ref0 = ::Prev; + } + } +} + "#, + ); +} + +#[test] +fn array_repeat_closure() { + check( + r#" +fn f() {[_; || ()]} + // ^^^^^^^^^^ expected (), got [{unknown}; _] + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index fb00a755fa055..121e3959ce23a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -1,6 +1,55 @@ use expect_test::expect; +use hir_def::ModuleDefId; +use rustc_type_ir::inherent::IntoKind as _; +use test_fixture::WithFixture; -use crate::tests::{check_infer, check_no_mismatches, check_types}; +use crate::{ + db::HirDatabase, + next_solver::{DbInterner, RegionKind, TyKind}, + test_db::TestDB, + tests::{check_infer, check_no_mismatches, check_types}, +}; + +#[test] +fn liberating_distinct_late_bound_lifetimes_preserves_identity() { + let (db, file_id) = TestDB::with_single_file( + r#" +fn f<'a, 'b>(x: &'a u8, y: &'b u8) {} +"#, + ); + + crate::attach_db(&db, || { + let module_id = db.module_for_file(file_id.file_id(&db)); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id].scope; + let func = scope + .declarations() + .find_map( + |decl| { + if let ModuleDefId::FunctionId(func) = decl { Some(func) } else { None } + }, + ) + .unwrap(); + let interner = DbInterner::new_with(&db, module_id.krate(&db)); + let sig = db.callable_item_signature(func.into()).instantiate_identity().skip_norm_wip(); + let sig = interner.liberate_late_bound_regions(func.into(), sig); + let inputs = sig.inputs(); + let TyKind::Ref(first_region, _first_ty, _first_mutability) = inputs[0].kind() else { + panic!("expected reference input, got {:?}", inputs[0]); + }; + let TyKind::Ref(second_region, _second_ty, _second_mutability) = inputs[1].kind() else { + panic!("expected reference input, got {:?}", inputs[1]); + }; + let RegionKind::ReLateParam(_first_late_param) = first_region.kind() else { + panic!("expected late parameter region, got {first_region:?}"); + }; + let RegionKind::ReLateParam(_second_late_param) = second_region.kind() else { + panic!("expected late parameter region, got {second_region:?}"); + }; + + assert_ne!(first_region, second_region); + }); +} #[test] fn regression_20365() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index b54ed080315e0..d7b7e4783978e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3253,9 +3253,9 @@ fn main() { "#, expect![[r#" 104..108 'self': &'? Box - 188..192 'self': &'a Box> + 188..192 'self': &'_ Box> 218..220 '{}': &'? T - 242..246 'self': &'a Box> + 242..246 'self': &'_ Box> 275..277 '{}': &'? Foo 297..301 'self': Box> 322..324 '{}': Foo @@ -3270,7 +3270,7 @@ fn main() { 389..394 'boxed': Box> 389..406 'boxed....nner()': &'? i32 416..421 'good1': &'? i32 - 424..438 'Foo::get_inner': fn get_inner(&'? Box>) -> &'? i32 + 424..438 'Foo::get_inner': fn get_inner(&'?0.0 Box>) -> &'?0.0 i32 424..446 'Foo::g...boxed)': &'? i32 439..445 '&boxed': &'? Box> 440..445 'boxed': Box> @@ -3278,7 +3278,7 @@ fn main() { 464..469 'boxed': Box> 464..480 'boxed....self()': &'? Foo 490..495 'good2': &'? Foo - 498..511 'Foo::get_self': fn get_self(&'? Box>) -> &'? Foo + 498..511 'Foo::get_self': fn get_self(&'?0.0 Box>) -> &'?0.0 Foo 498..519 'Foo::g...boxed)': &'? Foo 512..518 '&boxed': &'? Box> 513..518 'boxed': Box> @@ -4323,3 +4323,39 @@ fn foo() { "#, ); } + +#[test] +fn type_alias_with_different_lifetime_name() { + check_infer( + r#" +trait Trait<'t> { + type Assoc<'a> where Self: 'a; +} + +struct Foo; + +impl<'u> Trait<'u> for Foo { + type Assoc<'b> = &'b u32; +} + +type Alias<'y, 'z> = >::Assoc<'z>; + +fn foo<'e, 'f>(alias: Alias<'e, 'f>) -> &'e u32 { + &1u32 +} + +fn check() { + let foo_fn = foo; +} +"#, + expect![[r#" + 199..204 'alias': &'_ u32 + 232..245 '{ &1u32 }': &'e u32 + 238..243 '&1u32': &'? u32 + 239..243 '1u32': u32 + 258..283 '{ ...foo; }': () + 268..274 'foo_fn': fn foo<'?>(>::Assoc<'?0.0>) -> &'? u32 + 277..280 'foo': fn foo<'?>(>::Assoc<'?0.0>) -> &'? u32 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 85c93abcf9349..6e61fcaa5d70b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4317,9 +4317,9 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { "#, expect![[r#" 90..94 'self': &'? Self - 127..128 'v': &'? (dyn Trait = &'a i32> + 'static) + 127..128 'v': &'? (dyn Trait = &'_ i32> + 'static) 164..195 '{ ...f(); }': () - 170..171 'v': &'? (dyn Trait = &'a i32> + 'static) + 170..171 'v': &'? (dyn Trait = &'_ i32> + 'static) 170..184 'v.get::()': <{unknown} as Trait>::Assoc 170..192 'v.get:...eref()': {unknown} "#]], @@ -4763,21 +4763,21 @@ fn f() { Struct::::IS_SEND; //^^^^^^^^^^^^^^^^^^^^Yes Struct::::IS_SEND; - //^^^^^^^^^^^^^^^^^^^^Yes + //^^^^^^^^^^^^^^^^^^^^{unknown} Struct::<*const T>::IS_SEND; - //^^^^^^^^^^^^^^^^^^^^^^^^^^^Yes + //^^^^^^^^^^^^^^^^^^^^^^^^^^^{unknown} Enum::::IS_SEND; //^^^^^^^^^^^^^^^^^^Yes Enum::::IS_SEND; - //^^^^^^^^^^^^^^^^^^Yes + //^^^^^^^^^^^^^^^^^^{unknown} Enum::<*const T>::IS_SEND; - //^^^^^^^^^^^^^^^^^^^^^^^^^Yes + //^^^^^^^^^^^^^^^^^^^^^^^^^{unknown} Union::::IS_SEND; //^^^^^^^^^^^^^^^^^^^Yes Union::::IS_SEND; - //^^^^^^^^^^^^^^^^^^^Yes + //^^^^^^^^^^^^^^^^^^^{unknown} Union::<*const T>::IS_SEND; - //^^^^^^^^^^^^^^^^^^^^^^^^^^Yes + //^^^^^^^^^^^^^^^^^^^^^^^^^^{unknown} PhantomData::::IS_SEND; //^^^^^^^^^^^^^^^^^^^^^^^^^Yes PhantomData::::IS_SEND; @@ -4883,6 +4883,23 @@ fn allowed3(baz: impl Baz>) {} ) } +#[test] +fn rpit_with_lifetimes() { + check_no_mismatches( + r#" +struct Event<'a> {}; +struct Range {} +trait Iterator { + type Item; +} + +struct Vec {} + +fn foo<'e>(events: &'e mut dyn Iterator, Range)>) -> impl Iterator> {} +"#, + ); +} + #[test] fn recursive_tail_sized() { check_infer( @@ -5260,3 +5277,65 @@ fn foo() { "#]], ); } + +#[test] +fn rpit_with_type_and_only_late_bound_lifetime() { + check_no_mismatches( + r#" +trait Trait<'a> {} +struct Foo {} + +impl<'a> Trait for () {} + +fn foo<'a, T>(t: &'a mut T) -> impl Trait<'a> {} + +fn bar() { + let mut f = Foo {}; + let p = foo(&mut f); +} +"#, + ); +} + +#[test] +fn rpit_with_type_and_both_lifetimes() { + check_no_mismatches( + r#" +trait Trait<'a> {} +struct Foo {} + +impl<'a> Trait for () {} + +fn foo<'a, 'b, T: 'b>(t: &'a mut T) -> impl Trait<'a> {} + +fn bar() { + let mut f = Foo {}; + let p = foo(&mut f); +} +"#, + ); +} + +#[test] +fn async_impl_trait() { + check_no_mismatches( + r#" +//- minicore: future +trait Reader {} + +struct Path {} +struct Result { v: T } + +impl Reader for () {} + +async fn read<'a>(path: &'a Path) -> Result { + Result { v: () } +} + +fn foo() { + let p = Path {}; + let v = read(&p); +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 2ca9ebe070bc9..935f541841d26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -24,7 +24,7 @@ use rustc_type_ir::{ }; use crate::{ - LifetimeElisionKind, Span, TyLoweringContext, + LifetimeElisionKind, LifetimeLoweringMode, Span, TyLoweringContext, db::HirDatabase, generics::Generics, lower::LoweringMode, @@ -192,6 +192,7 @@ pub fn where_predicate_must_hold<'db>( generic_def, &generics, LifetimeElisionKind::Infer, + LifetimeLoweringMode::Bound, ) .with_interning_mode(LoweringMode::Ide); let clauses = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 0a95416e42560..9e04353087760 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -58,7 +58,7 @@ fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariance } let generics = generics(db, def); - let count = generics.len(); + let count = generics.len(true); if count == 0 { return VariancesOf::empty(DbInterner::new_no_crate(db)).store(); } @@ -106,7 +106,7 @@ pub(crate) fn variances_of_cycle_initial( ) -> StoredVariancesOf { let interner = DbInterner::new_no_crate(db); let generics = generics(db, def); - let count = generics.len(); + let count = generics.len(true); VariancesOf::new_from_iter(interner, std::iter::repeat_n(Variance::Bivariant, count)).store() } @@ -152,7 +152,7 @@ impl<'db> Context<'db> { // Const parameters are always invariant. // Make all const parameters invariant. - for (idx, param) in self.generics.iter_id().enumerate() { + for (idx, param) in self.generics.iter_id(false).enumerate() { if let GenericParamId::ConstParamId(_) = param { variances[idx] = Variance::Invariant; } @@ -940,7 +940,7 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); res, "{name}[{}]\n", generics(&db, def) - .iter() + .iter(false) .map(|(_, param)| match param { GenericParamDataRef::TypeParamData(type_param_data) => { type_param_data.name.as_ref().unwrap() diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index cfb967f486247..b921a5eef14a9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -36,7 +36,7 @@ use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwne pub use hir_def::VariantId; pub use hir_ty::{ - GenericArgsProhibitedReason, IncorrectGenericsLenKind, + GenericArgsProhibitedReason, IncorrectGenericsLenKind, ReturnKind, diagnostics::{CaseType, IncorrectCase}, }; @@ -181,6 +181,7 @@ diagnostics![AnyDiagnostic<'db> -> UnionPatHasRest, UnimplementedTrait<'db>, YieldOutsideCoroutine, + ReturnOutsideFunction, ]; #[derive(Debug)] @@ -689,6 +690,12 @@ pub struct YieldOutsideCoroutine { pub expr: InFile, } +#[derive(Debug)] +pub struct ReturnOutsideFunction { + pub expr: InFile, + pub kind: ReturnKind, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -1131,6 +1138,9 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::YieldOutsideCoroutine { expr } => { YieldOutsideCoroutine { expr: expr_syntax(expr)? }.into() } + &InferenceDiagnostic::ReturnOutsideFunction { expr, kind } => { + ReturnOutsideFunction { expr: expr_syntax(expr)?, kind }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 0095401ff5ee8..06fa91804ddee 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -14,6 +14,7 @@ use hir_def::{ TraitSignature, TypeAliasSignature, }, type_ref::{TypeBound, TypeRef, TypeRefId}, + visibility::Visibility, }; use hir_expand::name::Name; use hir_ty::{ @@ -363,20 +364,22 @@ impl<'db> HirDisplay<'db> for Struct { let def_id = GenericDefId::AdtId(AdtId::StructId(self.id)); write_generic_params(def_id, f)?; - let variant_data = self.variant_fields(f.db); match self.kind(f.db) { StructKind::Tuple => { f.write_char('(')?; - let mut it = variant_data.fields().iter().peekable(); + let (fields, hidden_fields) = visible_fields(self.fields(f.db), f); + let mut it = fields.iter().peekable(); - while let Some((id, _)) = it.next() { - let field = Field { parent: (*self).into(), id }; + while let Some(field) = it.next() { write_visibility(module_id, field.visibility(f.db), f)?; field.ty(f.db).hir_fmt(f)?; - if it.peek().is_some() { + if it.peek().is_some() || hidden_fields { f.write_str(", ")?; } } + if hidden_fields { + f.write_str("/* … */")?; + } f.write_char(')')?; write_where_clause(def_id, f)?; @@ -384,7 +387,8 @@ impl<'db> HirDisplay<'db> for Struct { StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; + let (fields, hidden_fields) = visible_fields(self.fields(f.db), f); + write_fields(&fields, hidden_fields, has_where_clause, limit, false, f)?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, @@ -421,14 +425,33 @@ impl<'db> HirDisplay<'db> for Union { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; + let (fields, hidden_fields) = visible_fields(self.fields(f.db), f); + write_fields(&fields, hidden_fields, has_where_clause, limit, false, f)?; } Ok(()) } } +fn visible_fields<'db>(fields: Vec, f: &mut HirFormatter<'_, 'db>) -> (Vec, bool) { + if f.render_private_fields() { + return (fields, false); + } + + let mut hidden_fields = false; + let fields = fields + .into_iter() + .filter(|field| { + let is_public = field.visibility(f.db) == Visibility::Public; + hidden_fields |= !is_public; + is_public + }) + .collect(); + (fields, hidden_fields) +} + fn write_fields<'db>( fields: &[Field], + hidden_fields: bool, has_where_clause: bool, limit: usize, in_line: bool, @@ -438,7 +461,7 @@ fn write_fields<'db>( let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') }; f.write_char(if !has_where_clause { ' ' } else { separator })?; if count == 0 { - f.write_str(if fields.is_empty() { "{}" } else { "{ /* … */ }" })?; + f.write_str(if fields.is_empty() && !hidden_fields { "{}" } else { "{ /* … */ }" })?; } else { f.write_char('{')?; @@ -450,7 +473,7 @@ fn write_fields<'db>( write!(f, ",{separator}")?; } - if fields.len() > count { + if fields.len() > count || hidden_fields { write!(f, "{indent}/* … */{separator}")?; } } @@ -542,7 +565,8 @@ impl<'db> HirDisplay<'db> for EnumVariant { } FieldsShape::Record => { if let Some(limit) = f.entity_limit { - write_fields(&self.fields(f.db), false, limit, true, f)?; + let (fields, hidden_fields) = visible_fields(self.fields(f.db), f); + write_fields(&fields, hidden_fields, false, limit, true, f)?; } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index fc04ea1f4978e..1ba73c180c319 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -87,15 +87,18 @@ use hir_ty::{ GenericPredicates, InferBodyId, InferenceResult, ParamEnvAndCrate, TyDefId, TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef, check_orphan_rules, consteval::try_const_usize, - db::{AnonConstId, InternedClosure, InternedClosureId, InternedCoroutineClosureId}, + db::{ + AnonConstId, InternedClosure, InternedClosureId, InternedCoroutineClosureId, + InternedCoroutineId, + }, diagnostics::BodyValidationDiagnostic, direct_super_traits, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution::{self, InherentImpls, MethodResolutionContext}, mir::interpret_mir, next_solver::{ - AliasTy, AnyImplId, ClauseKind, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, - GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode, + AliasTy, AnyImplId, ClauseKind, DbInterner, EarlyBinder, ErrorGuaranteed, FnSig, + GenericArg, GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, traits::{self, is_inherent_impl_coherent, structurally_normalize_ty}, @@ -668,7 +671,7 @@ impl Module { while id.is_block_module(db) { id = id.containing_module(db).expect("block without parent module"); } - Module { id } + Module { id: unsafe { id.to_static() } } } pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec { @@ -1797,7 +1800,7 @@ impl Adt { resolver .generic_params() .and_then(|gp| { - gp.iter_lt() + gp.iter_early_bound_lt() // there should only be a single lifetime // but `Arena` requires to use an iterator .nth(0) @@ -2097,8 +2100,8 @@ impl DefWithBody { } } - if let Ok(borrowck_results) = db.borrowck(id.into()) { - for borrowck_result in borrowck_results.iter() { + if let Ok(borrowck_results) = InferBodyId::from(id).borrowck(db) { + for borrowck_result in borrowck_results { let mir_body = borrowck_result.mir_body(db); for moof in &borrowck_result.moved_out_of_ref { let span: InFile = match moof.span { @@ -2380,10 +2383,16 @@ impl Function { (fn_ptr.owner, sig_tys.with(hdr)) } + fn erased_fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (TypeOwnerId, FnSig<'db>) { + let (owner, sig) = self.fn_sig(db); + let sig = DbInterner::new_no_crate(db).instantiate_bound_regions_with_erased(sig); + (owner, sig) + } + /// Get this function's return type pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> { - let (owner, sig) = self.fn_sig(db); - Type { owner, ty: EarlyBinder::bind(sig.skip_binder().output()) } + let (owner, sig) = self.erased_fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.output()) } } pub fn async_ret_type<'db>(self, db: &'db dyn HirDatabase) -> Option> { @@ -2393,10 +2402,12 @@ impl Function { if !self.is_async(db) { return None; } - let ret_ty = - db.callable_item_signature(id.into()).instantiate_identity().skip_binder().output(); + let interner = DbInterner::new_no_crate(db); + let sig = db.callable_item_signature(id.into()).instantiate_identity().skip_norm_wip(); + let ret_ty = interner.instantiate_bound_regions_with_erased(sig).output(); for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() { - if let ClauseKind::Projection(projection) = pred.kind().skip_binder() + let clause = interner.instantiate_bound_regions_with_erased(pred.kind()); + if let ClauseKind::Projection(projection) = clause && let Some(output_ty) = projection.term.as_type() { return Some(Type::new(id.into(), output_ty)); @@ -2425,15 +2436,14 @@ impl Function { } pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec> { - let (owner, sig) = self.fn_sig(db); + let (owner, sig) = self.erased_fn_sig(db); let func = match self.id { AnyFunctionId::FunctionId(id) => Callee::Def(CallableDefId::FunctionId(id)), AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { Callee::BuiltinDeriveImplMethod { method, impl_ } } }; - sig.skip_binder() - .inputs() + sig.inputs() .iter() .enumerate() .map(|(idx, &ty)| Param { @@ -2819,8 +2829,8 @@ impl SelfParam { } pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { - let (owner, sig) = self.func.fn_sig(db); - Type { owner, ty: EarlyBinder::bind(sig.skip_binder().inputs()[0]) } + let (owner, sig) = self.func.erased_fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.inputs()[0]) } } } @@ -4714,7 +4724,7 @@ impl Impl { module.block(db), &mut |impls| extend_with_impls(Either::Left(impls.for_self_ty(&simplified_ty))), ); - iter::successors(module.block(db), |block| block.loc(db).module.block(db)) + std::iter::successors(module.block(db), |block| block.module(db).block(db)) .filter_map(|block| TraitImpls::for_block(db, block)) .for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls)); for &krate in &*all_crates(db) { @@ -4930,15 +4940,7 @@ impl<'db> Closure<'db> { AnyClosureId::ClosureId(it) => it.loc(db), AnyClosureId::CoroutineClosureId(it) => it.loc(db), }; - let InternedClosure { owner: infer_owner, expr: closure, .. } = closure; - let infer = InferenceResult::of(db, infer_owner); - let owner = infer_owner.expression_store_owner(db); - infer.closures_data[&closure] - .min_captures - .values() - .flatten() - .map(|capture| ClosureCapture { owner, infer_owner, closure, capture }) - .collect() + captured_items(db, closure) } pub fn fn_trait(&self, _db: &dyn HirDatabase) -> FnTrait { @@ -4957,6 +4959,34 @@ impl<'db> Closure<'db> { } } +/// A coroutine expression, including async, generator, and async-generator coroutines. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Coroutine { + id: InternedCoroutineId, +} + +impl Coroutine { + /// Returns the values captured by this coroutine. + pub fn captured_items<'db>(&self, db: &'db dyn HirDatabase) -> Vec> { + captured_items(db, self.id.loc(db)) + } +} + +fn captured_items<'db>( + db: &'db dyn HirDatabase, + closure: InternedClosure, +) -> Vec> { + let InternedClosure { owner: infer_owner, expr: closure, .. } = closure; + let infer = InferenceResult::of(db, infer_owner); + let owner = infer_owner.expression_store_owner(db); + infer.closures_data[&closure] + .min_captures + .values() + .flatten() + .map(|capture| ClosureCapture { owner, infer_owner, closure, capture }) + .collect() +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum FnTrait { FnOnce, @@ -5414,7 +5444,7 @@ impl<'db> Type<'db> { TypeOwnerId::AnonConstId(def) => def.into(), TypeOwnerId::NoParams(_) => return ty.ty.skip_binder(), }; - let args = GenericArgs::for_item(infcx.interner, owner, |_, param, _| { + let args = GenericArgs::for_item(infcx.interner, owner, |_, param, _, _| { *var_for_param .entry(param) .or_insert_with(|| infcx.var_for_def(param, hir_ty::Span::Dummy)) @@ -5855,7 +5885,7 @@ impl<'db> Type<'db> { let env = ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate: self.krate(db) }; traits::implements_trait_unique_with_infcx(db, env, trait_.id, &mut |infcx| { let mut args = Self::instantiate_many_with_infer(iter::once(self).chain(args), infcx); - GenericArgs::for_item(infcx.interner, trait_.id.into(), |_, param, _| { + GenericArgs::for_item(infcx.interner, trait_.id.into(), |_, param, _, _| { if let GenericParamId::TypeParamId(_) = param && let Some(arg) = args.next() { @@ -5945,6 +5975,14 @@ impl<'db> Type<'db> { } } + /// Returns this type as a coroutine. + pub fn as_coroutine(&self) -> Option { + match self.ty.skip_binder().kind() { + TyKind::Coroutine(id, _) => Some(Coroutine { id: id.0 }), + _ => None, + } + } + pub fn is_fn(&self) -> bool { matches!(self.ty.skip_binder().kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) } @@ -6685,6 +6723,10 @@ pub enum CallableKind<'db> { } impl<'db> Callable<'db> { + fn erased_sig(&self) -> FnSig<'db> { + DbInterner::conjure().instantiate_bound_regions_with_erased(self.sig) + } + pub fn kind(&self) -> CallableKind<'db> { match self.callee { Callee::Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), @@ -6725,19 +6767,14 @@ impl<'db> Callable<'db> { return None; } let func = self.as_function()?; - Some(( - func.self_param(db)?, - self.ty.derived(self.sig.skip_binder().inputs_and_output.inputs()[0]), - )) + Some((func.self_param(db)?, self.ty.derived(self.erased_sig().inputs()[0]))) } pub fn n_params(&self) -> usize { self.sig.skip_binder().inputs_and_output.inputs().len() - if self.is_bound_method { 1 } else { 0 } } pub fn params(&self) -> Vec> { - self.sig - .skip_binder() - .inputs_and_output + self.erased_sig() .inputs() .iter() .enumerate() @@ -6747,7 +6784,7 @@ impl<'db> Callable<'db> { .collect() } pub fn return_type(&self) -> Type<'db> { - self.ty.derived(self.sig.skip_binder().output()) + self.ty.derived(self.erased_sig().output()) } pub fn sig(&self) -> impl Eq { &self.sig @@ -7413,7 +7450,7 @@ fn generic_args_from_tys<'db>( ) -> (GenericArgs<'db>, TypeOwnerId) { let mut owner = None::; let mut args = args.into_iter(); - let args = GenericArgs::for_item(interner, def_id, |_, id, _| { + let args = GenericArgs::for_item(interner, def_id, |_, id, _, _| { if matches!(id, GenericParamId::TypeParamId(_)) && let Some(arg) = args.next() { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index ba488b8af35e5..e9e6ec3a01249 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1823,11 +1823,14 @@ impl<'db> SemanticsImpl<'db> { let AnyFunctionId::FunctionId(func) = func.id else { return Some(func) }; let interner = DbInterner::new_no_crate(self.db); let mut subst = subst.into_iter(); - let substs = - hir_ty::next_solver::GenericArgs::for_item(interner, trait_.id.into(), |_, id, _| { + 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.skip_binder().into() - }); + }, + ); assert!(subst.next().is_none(), "too many subst"); Some(match self.db.lookup_impl_method(env.param_env(self.db), func, substs).0 { Either::Left(it) => it.into(), diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index bca8c8c503dfd..19aa1581318ba 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -228,7 +228,7 @@ impl ChildBySource for DefWithBodyId { // All block expressions are merged into the same map, because they logically all add // inner items to the containing `DefWithBodyId`. def_map[def_map.root].scope.child_by_source_to(db, res, file_id); - res[keys::BLOCK].insert(block.lookup(db).ast_id.to_ptr(db), block); + res[keys::BLOCK].insert(block.ast_id(db).to_ptr(db), block); } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 21830f9d0d78e..db4e92098e81f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -13,7 +13,8 @@ use std::{ use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, - FunctionId, GenericDefId, HasModule, LocalFieldId, ModuleDefId, StructId, VariantId, + FunctionId, GenericDefId, HasModule, LocalFieldId, LoweringMode, ModuleDefId, StructId, + VariantId, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, HygieneId, lower::{ExprCollector, lower_generic_params}, @@ -32,8 +33,8 @@ use hir_expand::{ name::{AsName, Name}, }; use hir_ty::{ - Adjustment, InferBodyId, InferenceResult, LifetimeElisionKind, ParamEnvAndCrate, - TyLoweringContext, TyLoweringInferVarsCtx, + Adjustment, InferBodyId, InferenceResult, LifetimeElisionKind, LifetimeLoweringMode, + ParamEnvAndCrate, TyLoweringContext, TyLoweringInferVarsCtx, diagnostics::{ InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields, unsafe_operations, @@ -378,8 +379,15 @@ impl<'db> SourceAnalyzer<'db> { }; let generic_def = owner.generic_def(db); let module = generic_def.module(db); - let (store, params, _) = - lower_generic_params(db, module, generic_def, self.file_id, None, Some(where_clause)); + let (store, params, _) = lower_generic_params( + db, + module, + generic_def, + self.file_id, + None, + Some(where_clause), + LoweringMode::Ide, + ); let predicates = params.where_predicates(); if predicates.is_empty() { return PredicateEvaluationResult::holds("predicate does not impose any obligations"); @@ -462,6 +470,7 @@ impl<'db> SourceAnalyzer<'db> { // (this can impact the lifetimes generated, e.g. in `const` they won't be `'static`, but this seems like a // small problem). LifetimeElisionKind::Infer, + LifetimeLoweringMode::LateParam, ) .with_infer_vars_behavior(Some(&mut vars_cts)) .lower_ty(type_ref); @@ -1274,7 +1283,8 @@ impl<'db> SourceAnalyzer<'db> { } // FIXME: collectiong here shouldnt be necessary? - let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id); + let mut collector = + ExprCollector::new(db, self.resolver.module(), self.file_id, LoweringMode::Ide); let hir_path = collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?; let parent_hir_path = path @@ -1479,7 +1489,8 @@ impl<'db> SourceAnalyzer<'db> { db: &dyn HirDatabase, path: &ast::Path, ) -> Option { - let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id); + let mut collector = + ExprCollector::new(db, self.resolver.module(), self.file_id, LoweringMode::Ide); let hir_path = collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?; let (store, _) = collector.store.finish(); @@ -1880,6 +1891,7 @@ fn resolve_hir_path_( def, &generics, LifetimeElisionKind::Infer, + LifetimeLoweringMode::LateParam, ) .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first())) @@ -2038,6 +2050,7 @@ fn resolve_hir_path_qualifier( def, &generics, LifetimeElisionKind::Infer, + LifetimeLoweringMode::LateParam, ) .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first())) diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 2b7f7da3bf0d3..8e107afc9e3d2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -48,7 +48,7 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)), ScopeDef::Local(it) => { if ctx.config.enable_borrowcheck { - let borrowck = db.borrowck(it.parent_infer).ok()?; + let borrowck = it.parent_infer.borrowck(db).ok()?; let invalid = borrowck.iter().any(|b| { let mir_body = b.mir_body(ctx.sema.db); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index e6d31b966099c..c9f5e0a4fbede 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -738,6 +738,25 @@ fn main() { ); } + #[test] + fn handles_closures_with_unannotated_rest_patterns() { + check_assist( + convert_closure_to_fn, + r#" +fn main() { + let closure = |$0..| (); +} +"#, + r#" +fn main() { + fn closure(..: _) { + () + } +} +"#, + ); + } + #[test] fn multiple_capture_usages() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index ecb031e42d7e9..329f8325b4c12 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -370,7 +370,7 @@ impl<'outer, Outer, const OUTER: usize> () { "#, r#" struct Struct; -type $0Type<'inner, 'outer, Outer, Inner, const INNER: usize, const OUTER: usize> = &(Struct, Struct, Outer, &'inner (), Inner, &'outer ()); +type $0Type<'inner, 'outer, Outer, Inner, const INNER: usize, const OUTER: usize> = &(Struct, Struct, Outer, &(), Inner, &'outer ()); impl<'outer, Outer, const OUTER: usize> () { fn func<'inner, Inner, const INNER: usize>(_: Type<'inner, 'outer, Outer, Inner, INNER, OUTER>) {} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 0bd9d1a23aeff..b75e7d4802d0a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1,15 +1,18 @@ +use std::ops::RangeInclusive; + use hir::{HirDisplay, TypeInfo}; use ide_db::{ assists::GroupLabel, syntax_helpers::{LexedStr, suggest_name}, }; use syntax::{ - Direction, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, T, TextRange, + Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, TextRange, algo::{ancestors_at_offset, skip_trivia_token}, ast::{ self, AstNode, edit::{AstNodeEdit, IndentLevel}, }, + hacks::parse_expr_from_str, syntax_editor::{Element, Position}, }; @@ -92,7 +95,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?; let range = node.text_range(); - let (to_replace, analysis) = if node.kind() == SyntaxKind::TOKEN_TREE { + let (to_replace, analysis, use_source_expr) = if node.kind() == SyntaxKind::TOKEN_TREE { let (first, last) = extract_token_range_of(&node, ctx.selection_trimmed())?; let first_descend = ctx.sema.descend_into_macros_single_exact(first.clone()); @@ -111,14 +114,14 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - if !node.text_range().contains_range(original_range.range) { return None; } - (cover_edit_range(&node, original_range.range), expr) + (cover_edit_range(&node, original_range.range), expr, true) } else { let expr = node .descendants() .take_while(|it| range.contains_range(it.text_range())) .find_map(valid_target_expr(ctx))?; let to_extract = expr.syntax().syntax_element(); - (to_extract.clone()..=to_extract, expr) + (to_extract.clone()..=to_extract, expr, false) }; let place = match to_replace.start() { NodeOrToken::Node(node) => node.clone(), @@ -217,6 +220,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - editor.add_annotation(pat_name.syntax().clone(), tabstop); } + let to_extract_no_ref = if use_source_expr { + source_expr(ctx, to_replace.clone()).unwrap() + } else { + to_extract_no_ref.clone() + }; let initializer = match ty.as_ref().filter(|_| needs_ref) { Some(receiver_type) if receiver_type.is_mutable_reference() => { make.expr_ref(to_extract_no_ref.clone(), true) @@ -331,6 +339,15 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr { expr } +fn source_expr( + ctx: &AssistContext<'_, '_>, + range: RangeInclusive, +) -> Option { + let range = range.start().text_range().cover(range.end().text_range()); + let text = ctx.source_file().syntax().text().slice(range).to_string(); + parse_expr_from_str(&text, ctx.edition()) +} + /// Check whether the node is a valid expression which can be extracted to a variable. /// In general that's true for any expression, but in some cases that would produce invalid code. fn valid_target_expr(ctx: &AssistContext<'_, '_>) -> impl Fn(SyntaxNode) -> Option { @@ -2828,7 +2845,6 @@ fn main() { #[test] fn extract_variable_in_token_tree() { - // FIXME: Keep the original trivia instead of extracting macro expanded? check_assist_by_label( extract_variable, r#" @@ -2850,7 +2866,7 @@ macro_rules! foo { } fn main() { - let $0var_name = 2+3; + let $0var_name = 2 + 3; let x = foo!(= var_name + 4); } "#, @@ -2878,7 +2894,7 @@ macro_rules! foo { } fn main() { - let $0var_name = 2+3; + let $0var_name = 2 + 3; let x = foo!(= var_name + 4); } "#, @@ -2906,7 +2922,7 @@ macro_rules! foo { } fn main() { - let $0var_name = 2+3+4; + let $0var_name = 2 + 3 + 4; let x = foo!(= var_name); } "#, @@ -2937,11 +2953,39 @@ macro_rules! foo { } fn main() { - let $0var_name = 2+3+4; + let $0var_name = 2 + 3 + 4; let x = foo!(= { var_name }); } +"#, + "Extract into variable", + ); + + check_assist_by_label( + extract_variable, + r#" +macro_rules! identity { + ($e:expr) => { + $e + }; +} + +fn main() { + let x = identity!($0(1+2)$0); +} +"#, + r#" +macro_rules! identity { + ($e:expr) => { + $e + }; +} + +fn main() { + let $0var_name = (1+2); + let x = identity!(var_name); +} "#, "Extract into variable", ); @@ -2970,7 +3014,7 @@ macro_rules! foo { } fn main() { - let $0x = 2+3; + let $0x = 2 + 3; let x = foo!(= Foo { x: x }); } "#, @@ -2998,7 +3042,7 @@ macro_rules! foo { } fn main() { - let $0var_name = 2+3; + let $0var_name = 2 + 3; let x = foo!(= Foo { x: var_name + 4 }); } "#, @@ -3006,6 +3050,51 @@ fn main() { ); } + #[test] + fn extract_variable_in_assert_macro_preserves_required_whitespace() { + check_assist_by_label( + extract_variable, + r#" +//- minicore: assert +fn check(value: &mut usize) -> bool { + false +} + +fn foo(mut bar: usize) { + assert!(check($0&mut bar$0)); +} +"#, + r#" +fn check(value: &mut usize) -> bool { + false +} + +fn foo(mut bar: usize) { + let $0value = &mut bar; + assert!(check(value)); +} +"#, + "Extract into variable", + ); + + check_assist_by_label( + extract_variable, + r#" +//- minicore: assert +fn main() { + assert!($0if true {true} else {false}$0); +} +"#, + r#" +fn main() { + let $0var_name = if true {true} else {false}; + assert!(var_name); +} +"#, + "Extract into variable", + ); + } + #[test] fn regression_22441() { check_assist_by_label( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index a582af4e2ca65..bc296b05d485a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -831,6 +831,25 @@ fn main() { ); } + #[test] + fn regression_22759() { + check_assist( + unwrap_branch, + r#" +fn main() { + match () { + () $0=> let x = (), + } +} +"#, + r#" +fn main() { + let x = () +} +"#, + ); + } + #[test] fn simple_if_in_while_bad_cursor_position() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 59c6c55c22b95..774e14df48340 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -395,6 +395,27 @@ impl A { ) } + #[test] + fn method_completion_with_late_bound_lifetime_in_return_type() { + check_no_kw( + r#" +//- minicore: deref +struct RelPath; +struct StripPrefixError; +enum Result { Ok(T), Err(E) } +impl RelPath { + fn strip_prefix<'a>(&'a self) -> Result<&'a RelPath, StripPrefixError> { + let path: &RelPath = self.strip_$0; + loop {} + } +} +"#, + expect![[r#" + me strip_prefix() fn(&'a self) -> Result<&RelPath, StripPrefixError> + "#]], + ); + } + #[test] fn test_no_struct_field_completion_for_method_call() { check_no_kw( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index b350647b9a2bd..972cc6d32fef8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -262,6 +262,7 @@ fn import_on_the_fly<'db>( } }; let user_input_lowercased = potential_import_name.to_lowercase(); + let mut import_name_buffer = String::new(); let import_cfg = ctx.config.import_path_config(); @@ -276,9 +277,13 @@ fn import_on_the_fly<'db>( }) .filter(|import| filter_excluded_flyimport(ctx, import)) .sorted_by(|a, b| { - let key = |import_path| { + let mut key = |import_path| { ( - compute_fuzzy_completion_order_key(import_path, &user_input_lowercased), + compute_fuzzy_completion_order_key( + import_path, + &user_input_lowercased, + &mut import_name_buffer, + ), import_path, ) }; @@ -310,6 +315,7 @@ fn import_on_the_fly_pat_<'db>( ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), }; let user_input_lowercased = potential_import_name.to_lowercase(); + let mut import_name_buffer = String::new(); let cfg = ctx.config.import_path_config(); import_assets @@ -322,9 +328,13 @@ fn import_on_the_fly_pat_<'db>( && ctx.check_stability(original_item.attrs(ctx.db).as_ref()) }) .sorted_by(|a, b| { - let key = |import_path| { + let mut key = |import_path| { ( - compute_fuzzy_completion_order_key(import_path, &user_input_lowercased), + compute_fuzzy_completion_order_key( + import_path, + &user_input_lowercased, + &mut import_name_buffer, + ), import_path, ) }; @@ -351,6 +361,7 @@ fn import_on_the_fly_method<'db>( ImportScope::find_insert_use_container(&position, &ctx.sema)?; let user_input_lowercased = potential_import_name.to_lowercase(); + let mut import_name_buffer = String::new(); let cfg = ctx.config.import_path_config(); @@ -362,9 +373,13 @@ fn import_on_the_fly_method<'db>( }) .filter(|import| filter_excluded_flyimport(ctx, import)) .sorted_by(|a, b| { - let key = |import_path| { + let mut key = |import_path| { ( - compute_fuzzy_completion_order_key(import_path, &user_input_lowercased), + compute_fuzzy_completion_order_key( + import_path, + &user_input_lowercased, + &mut import_name_buffer, + ), import_path, ) }; @@ -437,15 +452,15 @@ fn import_assets_for_path<'db>( fn compute_fuzzy_completion_order_key( proposed_mod_path: &hir::ModPath, user_input_lowercased: &str, + import_name_buffer: &mut String, ) -> usize { cov_mark::hit!(certain_fuzzy_order_test); - let import_name = match proposed_mod_path.segments().last() { - // FIXME: nasty alloc, this is a hot path! - Some(name) => name.as_str().to_ascii_lowercase(), - None => return usize::MAX, + let Some(import_name) = proposed_mod_path.segments().last() else { + return usize::MAX; }; - match import_name.match_indices(user_input_lowercased).next() { - Some((first_matching_index, _)) => first_matching_index, - None => usize::MAX, - } + + import_name_buffer.clear(); + import_name_buffer.push_str(import_name.as_str()); + import_name_buffer.make_ascii_lowercase(); + import_name_buffer.find(user_input_lowercased).unwrap_or(usize::MAX) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 300ea9bb11032..bcc103b89fa11 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -1157,6 +1157,7 @@ mod derive { de PartialEq, Eq, PartialOrd, Ord de PartialEq, PartialOrd md core:: + md panic:: kw crate:: kw self:: "#]], @@ -1179,6 +1180,7 @@ mod derive { de Eq, PartialOrd, Ord de PartialOrd md core:: + md panic:: kw crate:: kw self:: "#]], @@ -1201,6 +1203,7 @@ mod derive { de Eq, PartialOrd, Ord de PartialOrd md core:: + md panic:: kw crate:: kw self:: "#]], @@ -1222,6 +1225,7 @@ mod derive { de PartialOrd de PartialOrd, Ord md core:: + md panic:: kw crate:: kw self:: "#]], diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index adf4dda18441f..0e558cf6a2b57 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3278,6 +3278,7 @@ fn bar() { ma panic!(…) macro_rules! panic ma print!(…) macro_rules! print md core:: + md panic:: md result:: (use core::result) md rust_2015:: (use core::prelude::rust_2015) md rust_2018:: (use core::prelude::rust_2018) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index dc162774a0a64..8a5025caf9012 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -248,6 +248,34 @@ fn main() { ); } +#[test] +fn fuzzy_completion_order_is_case_insensitive_and_deterministic() { + check( + r#" +//- /lib.rs crate:dep +pub mod zed { + pub struct HIRThing; +} +pub mod alpha { + pub struct HIRThing; +} +pub struct BeforeHIRThing; +pub struct HiiirThing; + +//- /main.rs crate:main deps:dep +fn main() { + hir$0 +} +"#, + expect![[r#" + st HIRThing (use dep::alpha::HIRThing) HIRThing + st HIRThing (use dep::zed::HIRThing) HIRThing + st BeforeHIRThing (use dep::BeforeHIRThing) BeforeHIRThing + st HiiirThing (use dep::HiiirThing) HiiirThing + "#]], + ); +} + #[test] fn trait_function_fuzzy_completion() { let fixture = r#" diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 0454f4e3504d4..bc7b7274a87ed 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -1581,7 +1581,7 @@ pub fn foo<'x, T>(x: &'x mut T) -> u8 where T: Clone, { 0u8 } fn main() { fo$0 } "#, CompletionItemKind::SymbolKind(ide_db::SymbolKind::Function), - expect!("fn(&'x mut T) -> u8"), + expect!("fn(&mut T) -> u8"), expect!("pub fn foo<'x, T>(x: &'x mut T) -> u8 where T: Clone,"), ); @@ -1614,7 +1614,7 @@ fn main() { } "#, CompletionItemKind::SymbolKind(SymbolKind::Method), - expect!("const fn(&'foo mut self, &'foo Foo) -> !"), + expect!("const fn(&'foo mut self, &Foo) -> !"), expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"), ); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 1a3fc0f358c1a..a38154420af1a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -232,17 +232,10 @@ pub struct SourceChangeBuilder { /// Keeps track of which annotations correspond to which snippets pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, - /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. - mutated_tree: Option, /// Keeps track of where to place snippets pub snippet_builder: Option, } -struct TreeMutator { - immutable: SyntaxNode, - mutable_clone: SyntaxNode, -} - #[derive(Default)] pub struct SnippetBuilder { /// Where to place snippets at @@ -258,7 +251,6 @@ impl SourceChangeBuilder { command: None, file_editors: FxHashMap::default(), snippet_annotations: vec![], - mutated_tree: None, snippet_builder: None, } } @@ -343,10 +335,6 @@ impl SourceChangeBuilder { ) }); - if let Some(tm) = self.mutated_tree.take() { - diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit); - } - let edit = mem::take(&mut self.edit).finish(); if !edit.is_empty() || snippet_edit.is_some() { self.source_change.insert_source_and_snippet_edit(self.file_id, edit, snippet_edit); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs index 8cae405c92eef..bd02ce6a02c7c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs @@ -111,4 +111,15 @@ fn f(arr: [i32; 3]) { "#, ); } + + #[test] + fn destructuring_assignment_array_rest() { + check_diagnostics( + r#" +fn main() { + [..] = [1, 2, 3]; +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/return_outside_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/return_outside_function.rs new file mode 100644 index 0000000000000..e6620ac5434be --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/return_outside_function.rs @@ -0,0 +1,98 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: return-outside-of-function +// +// This diagnostic triggers if return or become is used outside of a function body. +pub(crate) fn return_outside_function( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ReturnOutsideFunction, +) -> Diagnostic { + let construct = match d.kind { + hir::ReturnKind::ReturnExpr => "return", + hir::ReturnKind::BecomeExpr => "become", + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0572"), + format!("{construct} statement outside of function body"), + d.expr.map(|it| it.into()), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn return_in_const() { + check_diagnostics( + r#" +const _: () = { + return; + //^^^^^^ error: return statement outside of function body +}; +"#, + ); + } + + #[test] + fn return_in_static() { + check_diagnostics( + r#" +static _S: i32 = { + return 0; + //^^^^^^^^ error: return statement outside of function body + 0 +}; +"#, + ); + } + + #[test] + fn return_in_function_is_correct() { + check_diagnostics( + r#" +fn foo() -> i32 { + if true { return 42; } + 0 +} +"#, + ); + } + + #[test] + fn become_in_const() { + check_diagnostics( + r#" +const _: () = { + become 0; + //^^^^^^^^ error: become statement outside of function body +}; +"#, + ); + } + + #[test] + fn become_in_static() { + check_diagnostics( + r#" +static _S: () = { + become 0; + //^^^^^^^^ error: become statement outside of function body + () +}; +"#, + ); + } + + #[test] + fn become_in_function_is_correct() { + check_diagnostics( + r#" +fn foo() { + if true { become (); } +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index afc74445f4297..dede3b4aad5af 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -378,19 +378,6 @@ fn main() { ); } - // regression test as we used to panic in this scenario - #[test] - fn unknown_struct_pattern_param_type() { - check_diagnostics( - r#" -struct S { field : u32 } -fn f(S { field }: error) { - // ^^^^^ 💡 warn: unused variable -} -"#, - ); - } - #[test] fn crate_attrs_lint_smoke_test() { check_diagnostics( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 8d8ed9acaa0fc..819b644dfadde 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -79,6 +79,7 @@ mod handlers { pub(crate) mod remove_trailing_return; pub(crate) mod remove_unnecessary_else; pub(crate) mod replace_filter_map_next_with_find_map; + pub(crate) mod return_outside_function; pub(crate) mod trait_impl_incorrect_safety; pub(crate) mod trait_impl_missing_assoc_item; pub(crate) mod trait_impl_orphan; @@ -560,6 +561,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d), AnyDiagnostic::YieldOutsideCoroutine(d) => handlers::yield_outside_coroutine::yield_outside_coroutine(&ctx, &d), + AnyDiagnostic::ReturnOutsideFunction(d) => handlers::return_outside_function::return_outside_function(&ctx, &d), }; res.push(d) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index c3a8e0362fee8..28e1dc72bf4cd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -481,6 +481,10 @@ pub(crate) fn hover_for_definition( }; let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default(); let subst_types = subst.map(|subst| subst.types(db)); + let render_private_fields = sema.scope(scope_node).is_some_and(|scope| { + def.krate(db) + .is_some_and(|def_crate| should_render_private_fields(db, def_crate, scope.krate())) + }); let (markup, range_map) = render::definition( sema.db, @@ -489,6 +493,7 @@ pub(crate) fn hover_for_definition( ¬able_traits, macro_arm, render_extras, + render_private_fields, subst_types.as_ref(), config, edition, @@ -508,6 +513,27 @@ pub(crate) fn hover_for_definition( } } +/// | Hover location | Definition location | Render private fields? | +/// |---|---:|---:| +/// | Workspace crate | Workspace crate | Yes | +/// | Workspace crate | External/library crate | No | +/// | External/library crate | Same external/library crate | Yes | +/// | External/library crate | Different external/library crate | No | +/// | Anywhere | Same crate as definition | Yes | +fn should_render_private_fields( + db: &RootDatabase, + def_crate: hir::Crate, + hover_crate: hir::Crate, +) -> bool { + let is_workspace_crate = |db: &RootDatabase, krate: hir::Crate| { + let origin = krate.origin(db); + !origin.is_lib() && !origin.is_lang() + }; + + def_crate == hover_crate + || is_workspace_crate(db, def_crate) && is_workspace_crate(db, hover_crate) +} + fn notable_traits<'db>( db: &'db RootDatabase, ty: &hir::Type<'db>, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index fe94e169ed9a9..638ea6bdb9773 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -326,13 +326,7 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option return None, }; - let tmp; - let needle = if is_clippy { - tmp = format!("clippy::{}", token.text()); - &tmp - } else { - token.text() - }; + let needle = if is_clippy { &format!("clippy::{}", token.text()) } else { token.text() }; let lint = lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?; @@ -456,6 +450,7 @@ pub(super) fn definition( notable_traits: &[(Trait, Vec<(Option>, Name)>)], macro_arm: Option, render_extras: bool, + render_private_fields: bool, subst_types: Option<&Vec<(Symbol, Type<'_>)>>, config: &HoverConfig<'_>, edition: Edition, @@ -465,22 +460,27 @@ pub(super) fn definition( let label = match def { Definition::Trait(trait_) => trait_ .display_limited(db, config.max_trait_assoc_items_count, display_target) + .with_private_fields(render_private_fields) + .to_string(), + Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => adt + .display_limited(db, config.max_fields_count, display_target) + .with_private_fields(render_private_fields) + .to_string(), + Definition::EnumVariant(variant) => variant + .display_limited(db, config.max_fields_count, display_target) + .with_private_fields(render_private_fields) + .to_string(), + Definition::Adt(adt @ Adt::Enum(_)) => adt + .display_limited(db, config.max_enum_variants_count, display_target) + .with_private_fields(render_private_fields) .to_string(), - Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => { - adt.display_limited(db, config.max_fields_count, display_target).to_string() - } - Definition::EnumVariant(variant) => { - variant.display_limited(db, config.max_fields_count, display_target).to_string() - } - Definition::Adt(adt @ Adt::Enum(_)) => { - adt.display_limited(db, config.max_enum_variants_count, display_target).to_string() - } Definition::SelfType(impl_def) => { let self_ty = &impl_def.self_ty(db); match self_ty.as_adt() { - Some(adt) => { - adt.display_limited(db, config.max_fields_count, display_target).to_string() - } + Some(adt) => adt + .display_limited(db, config.max_fields_count, display_target) + .with_private_fields(render_private_fields) + .to_string(), None => self_ty.display(db, display_target).to_string(), } } @@ -496,7 +496,11 @@ pub(super) fn definition( } _ => def.label(db, display_target), }; - let docs = def.docs_with_rangemap(db, famous_defs, display_target); + let docs = if config.documentation { + def.docs_with_rangemap(db, famous_defs, display_target) + } else { + None + }; let value = || match def { Definition::EnumVariant(it) => { if !it.parent_enum(db).is_data_carrying(db) { @@ -525,7 +529,8 @@ pub(super) fn definition( let body = it.eval(db); Some(match body { Ok(it) => match it.render_debug(db) { - Ok(it) => it, + Ok(rendered) if rendered.is_empty() => it.render(db, display_target), + Ok(rendered) => rendered, Err(err) => { let it = it.render(db, display_target); if env::var_os("RA_DEV").is_some() { @@ -557,7 +562,9 @@ pub(super) fn definition( let body = it.eval(db); Some(match body { Ok(it) => match it.render_debug(db) { - Ok(it) => it, + Ok(rendered) if rendered.is_empty() => it.render(db, display_target), + Ok(rendered) => rendered, + Err(err) => { let it = it.render(db, display_target); if env::var_os("RA_DEV").is_some() { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 2bd79d79dfb13..89f1cf2fc1e11 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1,15 +1,15 @@ use expect_test::{Expect, expect}; use ide_db::{FileRange, base_db::SourceDatabase, ra_fixture::RaFixtureConfig}; +use span::FileId; use syntax::TextRange; use crate::{ - HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, fixture, + HoverAction, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + fixture, }; -use hir::setup_tracing; - const HOVER_BASE_CONFIG: HoverConfig<'_> = HoverConfig { - links_in_hover: false, + links_in_hover: true, memory_layout: Some(MemoryLayoutHoverConfig { size: Some(MemoryLayoutHoverRenderKind::Both), offset: Some(MemoryLayoutHoverRenderKind::Both), @@ -32,7 +32,7 @@ fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, + &HOVER_BASE_CONFIG, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap(); @@ -41,11 +41,19 @@ fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) { #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { - let _tracing = setup_tracing(); + check_with_config(ra_fixture, expect, &HOVER_BASE_CONFIG); +} + +#[track_caller] +fn check_with_config( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + expect: Expect, + config: &HoverConfig<'_>, +) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, + config, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -64,24 +72,11 @@ fn check_hover_fields_limit( #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, ) { - let (analysis, position) = fixture::position(ra_fixture); - let hover = analysis - .hover( - &HoverConfig { - links_in_hover: true, - max_fields_count: fields_count.into(), - ..HOVER_BASE_CONFIG - }, - FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, - ) - .unwrap() - .unwrap(); - - let content = analysis.db.file_text(position.file_id).text(&analysis.db); - let hovered_element = &content[hover.range]; - - let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); - expect.assert_eq(&actual) + check_with_config( + ra_fixture, + expect, + &HoverConfig { max_fields_count: fields_count.into(), ..HOVER_BASE_CONFIG }, + ); } #[track_caller] @@ -90,24 +85,11 @@ fn check_hover_enum_variants_limit( #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, ) { - let (analysis, position) = fixture::position(ra_fixture); - let hover = analysis - .hover( - &HoverConfig { - links_in_hover: true, - max_enum_variants_count: variants_count.into(), - ..HOVER_BASE_CONFIG - }, - FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, - ) - .unwrap() - .unwrap(); - - let content = analysis.db.file_text(position.file_id).text(&analysis.db); - let hovered_element = &content[hover.range]; - - let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); - expect.assert_eq(&actual) + check_with_config( + ra_fixture, + expect, + &HoverConfig { max_enum_variants_count: variants_count.into(), ..HOVER_BASE_CONFIG }, + ); } #[track_caller] @@ -116,92 +98,41 @@ fn check_assoc_count( #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, ) { - let (analysis, position) = fixture::position(ra_fixture); - let hover = analysis - .hover( - &HoverConfig { - links_in_hover: true, - max_trait_assoc_items_count: Some(count), - ..HOVER_BASE_CONFIG - }, - FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, - ) - .unwrap() - .unwrap(); - - let content = analysis.db.file_text(position.file_id).text(&analysis.db); - let hovered_element = &content[hover.range]; - - let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); - expect.assert_eq(&actual) + check_with_config( + ra_fixture, + expect, + &HoverConfig { max_trait_assoc_items_count: Some(count), ..HOVER_BASE_CONFIG }, + ); } fn check_hover_no_links(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { - let (analysis, position) = fixture::position(ra_fixture); - let hover = analysis - .hover( - &HOVER_BASE_CONFIG, - FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, - ) - .unwrap() - .unwrap(); - - let content = analysis.db.file_text(position.file_id).text(&analysis.db); - let hovered_element = &content[hover.range]; - - let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); - expect.assert_eq(&actual) + check_with_config( + ra_fixture, + expect, + &HoverConfig { links_in_hover: false, ..HOVER_BASE_CONFIG }, + ); } fn check_hover_no_memory_layout(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { - let (analysis, position) = fixture::position(ra_fixture); - let hover = analysis - .hover( - &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG }, - FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, - ) - .unwrap() - .unwrap(); - - let content = analysis.db.file_text(position.file_id).text(&analysis.db); - let hovered_element = &content[hover.range]; - - let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); - expect.assert_eq(&actual) + check_with_config( + ra_fixture, + expect, + &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG }, + ); } fn check_hover_no_markdown(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { - let (analysis, position) = fixture::position(ra_fixture); - let hover = analysis - .hover( - &HoverConfig { - links_in_hover: true, - format: HoverDocFormat::PlainText, - ..HOVER_BASE_CONFIG - }, - FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, - ) - .unwrap() - .unwrap(); - - let content = analysis.db.file_text(position.file_id).text(&analysis.db); - let hovered_element = &content[hover.range]; - - let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); - expect.assert_eq(&actual) + check_with_config( + ra_fixture, + expect, + &HoverConfig { format: HoverDocFormat::PlainText, ..HOVER_BASE_CONFIG }, + ); } -fn check_actions(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { - let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); - let mut hover = analysis - .hover( - &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, - FileRange { file_id, range: position.range_or_empty() }, - ) - .unwrap() - .unwrap(); +#[track_caller] +fn assert_actions(mut actions: Vec, file_id: FileId, expect: Expect) { // stub out ranges into minicore as they can change every now and then - hover.info.actions.iter_mut().for_each(|action| match action { + actions.iter_mut().for_each(|action| match action { super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { if data.nav.file_id == file_id { return; @@ -213,7 +144,16 @@ fn check_actions(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect }), _ => (), }); - expect.assert_debug_eq(&hover.info.actions) + expect.assert_debug_eq(&actions) +} + +fn check_actions(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); + let hover = analysis + .hover(&HOVER_BASE_CONFIG, FileRange { file_id, range: position.range_or_empty() }) + .unwrap() + .unwrap(); + assert_actions(hover.info.actions, file_id, expect); } fn check_hover_range(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { @@ -224,24 +164,8 @@ fn check_hover_range(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Ex fn check_hover_range_actions(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let (analysis, range) = fixture::range(ra_fixture); - let mut hover = analysis - .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range) - .unwrap() - .unwrap(); - // stub out ranges into minicore as they can change every now and then - hover.info.actions.iter_mut().for_each(|action| match action { - super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { - if data.nav.file_id == range.file_id { - return; - } - data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); - if let Some(range) = &mut data.nav.focus_range { - *range = TextRange::empty(span::TextSize::new(!0)); - } - }), - _ => (), - }); - expect.assert_debug_eq(&hover.info.actions); + let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap().unwrap(); + assert_actions(hover.info.actions, range.file_id, expect); } fn check_hover_range_no_results(#[rust_analyzer::rust_fixture] ra_fixture: &str) { @@ -994,6 +918,158 @@ struct Foo$0 where u32: Copy { field: u32 } ); } +#[test] +fn hover_filters_private_struct_fields_by_hover_context() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + let _: dep::Foo$0; +} + +//- /dep.rs crate:dep library +pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, +} +"#, + expect![[r#" + *Foo* + + ```rust + dep + ``` + + ```rust + pub struct Foo { + pub visible: i32, + /* … */ + } + ``` + "#]], + ); + + check( + r#" +//- /dep.rs crate:dep library +pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, +} + +fn main() { + let _: Foo$0; +} +"#, + expect![[r#" + *Foo* + + ```rust + dep + ``` + + ```rust + pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, + } + ``` + "#]], + ); + + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + let _: dep::Foo$0; +} + +//- /dep.rs crate:dep +pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, +} +"#, + expect![[r#" + *Foo* + + ```rust + dep + ``` + + ```rust + pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, + } + ``` + "#]], + ); +} + +#[test] +fn hover_filters_private_tuple_struct_fields_by_hover_context() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + let _: dep::Foo$0; +} + +//- /dep.rs crate:dep library +pub struct Foo(pub i32, i32, pub(crate) i32); +"#, + expect![[r#" + *Foo* + + ```rust + dep + ``` + + ```rust + pub struct Foo(pub i32, /* … */) + ``` + "#]], + ); +} + +#[test] +fn hover_filters_private_union_fields_by_hover_context() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + let _: dep::U$0; +} + +//- /dep.rs crate:dep library +pub union U { + pub visible: i32, + hidden: u32, +} +"#, + expect![[r#" + *U* + + ```rust + dep + ``` + + ```rust + pub union U { + pub visible: i32, + /* … */ + } + ``` + "#]], + ); +} + #[test] fn hover_record_struct_limit() { check_hover_fields_limit( @@ -6891,7 +6967,7 @@ fn hover_feature() { let (analysis, position) = fixture::position(r#"#![feature(intrinsics$0)]"#); analysis .hover( - &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, + &HOVER_BASE_CONFIG, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -11793,3 +11869,25 @@ pub struct Bar; "#]], ); } + +#[test] +fn respects_the_documentation_config() { + check_with_config( + r#" +/// Blah blah some docs. +fn foo() { foo$0(); } + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture + ``` + + ```rust + fn foo() + ``` + "#]], + &HoverConfig { documentation: false, ..HOVER_BASE_CONFIG }, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index a15366fea9621..98d5efb0f34d7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -235,9 +235,22 @@ fn hints( param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) } ast::Expr::ClosureExpr(it) => { - closure_captures::hints(hints, famous_defs, config, it.clone(), file_id.edition(sema.db)); + closure_captures::hints( + hints, + famous_defs, + config, + Either::Left(it.clone()), + file_id.edition(sema.db), + ); closure_ret::hints(hints, famous_defs, config, display_target, it) }, + ast::Expr::BlockExpr(it) => closure_captures::hints( + hints, + famous_defs, + config, + Either::Right(it), + file_id.edition(sema.db), + ), ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it), ast::Expr::Literal(it) => ra_fixture::hints(hints, famous_defs.0, file_id, config, it), _ => Some(()), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 57b723cbd8720..8af78532f3707 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -433,7 +433,7 @@ fn f<'a>() { let y = S::<'_>(loop {}); //^ S<'_> let z = S::<'a>(loop {}); - //^ S<'a> + //^ S<'_> } "#, @@ -1446,22 +1446,7 @@ fn f<'a>() { ), tooltip: "", }, - "<", - InlayHintLabelPart { - text: "'a", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 35..37, - }, - ), - ), - tooltip: "", - }, - ">", + "<'_>", ], ), ] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index c6c8806ac9894..836ebfbcf7441 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -75,18 +75,17 @@ pub(super) fn hints( node = node.parent()?; let parent = label.syntax().parent()?; - let block; - match_ast! { + let block = match_ast! { match parent { ast::BlockExpr(block_expr) => { - block = block_expr.stmt_list()?; + block_expr.stmt_list()? }, ast::AnyHasLoopBody(loop_expr) => { - block = loop_expr.loop_body()?.stmt_list()?; + loop_expr.loop_body()?.stmt_list()? }, _ => return None, } - } + }; closing_token = block.r_curly_token()?; let lifetime = label.lifetime()?.to_string(); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index df2c42a68c9f3..dc36bd776db6c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -1,6 +1,7 @@ //! Implementation of "closure captures" inlay hints. //! //! Tests live in [`bind_pat`][super::bind_pat] module. +use either::Either; use ide_db::famous_defs::FamousDefs; use span::Edition; use stdx::{TupleExt, never}; @@ -14,26 +15,56 @@ pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig<'_>, - closure: ast::ClosureExpr, + expr: Either, edition: Edition, ) -> Option<()> { if !config.closure_capture_hints { return None; } - let ty = &sema.type_of_expr(&closure.clone().into())?.original; - let c = ty.as_closure()?; - let captures = c.captured_items(sema.db); + + let (expr, move_token, capture_anchor) = match expr { + Either::Left(closure) => { + let move_token = closure.move_token(); + let capture_anchor = closure.param_list()?.pipe_token()?; + (closure.into(), move_token, capture_anchor) + } + Either::Right(block) => { + let modifier = block.modifier()?; + match modifier { + ast::BlockModifier::Async(_) + | ast::BlockModifier::Gen(_) + | ast::BlockModifier::AsyncGen(_) => (), + ast::BlockModifier::Unsafe(_) + | ast::BlockModifier::Try { .. } + | ast::BlockModifier::Const(_) + | ast::BlockModifier::Label(_) => return None, + } + let move_token = block.move_token(); + let capture_anchor = block.stmt_list()?.l_curly_token()?; + (block.into(), move_token, capture_anchor) + } + }; + + let ty = &sema.type_of_expr(&expr)?.original; + let captures = match ty.as_closure() { + Some(closure) => closure.captured_items(sema.db), + None => ty.as_coroutine()?.captured_items(sema.db), + }; if captures.is_empty() { return None; } - let (range, label, position, pad_right) = match closure.move_token() { - Some(t) => (t.text_range(), InlayHintLabel::default(), InlayHintPosition::After, false), - None => { - let l_pipe = closure.param_list()?.pipe_token()?.text_range(); - (l_pipe, InlayHintLabel::from("move"), InlayHintPosition::Before, true) + let (range, label, position, pad_right) = match move_token { + Some(token) => { + (token.text_range(), InlayHintLabel::default(), InlayHintPosition::After, false) } + None => ( + capture_anchor.text_range(), + InlayHintLabel::from("move"), + InlayHintPosition::Before, + true, + ), }; let mut hint = InlayHint { range, @@ -43,7 +74,7 @@ pub(super) fn hints( position, pad_left: false, pad_right, - resolve_parent: Some(closure.syntax().text_range()), + resolve_parent: Some(expr.syntax().text_range()), }; hint.label.append_str("("); let last = captures.len() - 1; @@ -186,6 +217,111 @@ fn main() { }; } +"#, + ); + } + + #[test] + fn all_capture_kinds_async_block() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, derive, future + +#[derive(Copy, Clone)] +struct Copy; + +struct NonCopy; + +fn main() { + let foo = Copy; + let bar = NonCopy; + let mut baz = NonCopy; + let qux = &mut NonCopy; + async { + // ^ move(&foo, bar, baz, qux) + foo; + bar; + baz; + qux; + }; + async { + // ^ move(&foo, &bar, &baz, &qux) + &foo; + &bar; + &baz; + &qux; + }; + async { + // ^ move(&mut baz) + &mut baz; + }; + async { + // ^ move(&mut baz, &mut *qux) + baz = NonCopy; + *qux = NonCopy; + }; +} +"#, + ); + } + + #[test] + fn nested_coroutine_does_not_capture_parent_local() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, future +fn main() { + async { + let foo = 1; + async { + // ^ move(&foo) + foo; + } + }; +} +"#, + ); + } + + #[test] + fn coroutine_blocks() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, future +fn main() { + let foo = 0; + gen { + // ^ move(&foo) + foo; + yield (); + }; + async gen { + // ^ move(&foo) + foo; + yield (); + }; +} +"#, + ); + } + + #[test] + fn legacy_coroutine() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, coroutine +fn main() { + let foo = 0; + let coroutine = #[coroutine] || { + // ^ move(&foo) + foo; + yield (); + }; +} "#, ); } @@ -216,6 +352,19 @@ fn main() { foo; }; } +"#, + ); + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, future +fn main() { + let foo = 0; + async move { + // ^^^^ (foo) + foo; + }; +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index fb082d3209b1e..f5c5cb432d559 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -408,6 +408,19 @@ impl SourceRootConfig { .collect() } + /// Returns whether `path` belongs to a library (non-local) source root, such as the + /// sysroot sources or a cargo registry dependency. + /// + /// Paths that belong to no configured file set are *not* considered library files, as + /// files outside of any loaded workspace (for example scratch files) fall into the + /// catch-all file set despite being client-editable. + pub fn path_is_library(&self, path: &VfsPath) -> bool { + match self.fsc.classify_path(path) { + Some(idx) => !self.local_filesets.contains(&(idx as u64)), + None => false, + } + } + /// Maps local source roots to their parent source roots by bytewise comparing of root paths . /// If a `SourceRoot` doesn't have a parent and is local then it is not contained in this mapping but it can be asserted that it is a root `SourceRoot`. pub fn source_root_parent_map(&self) -> FxHashMap { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs index db0185331cdba..26b53fab9f07b 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs @@ -393,14 +393,20 @@ pub(super) fn opt_type_bounds_as_dyn_trait_type( p: &mut Parser<'_>, type_marker: CompletedMarker, ) -> CompletedMarker { - assert!(matches!( - type_marker.kind(), - SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE - )); + assert!(matches!(type_marker.kind(), PATH_TYPE | FOR_TYPE | MACRO_TYPE)); if !p.at(T![+]) { return type_marker; } + // test_err macro_as_type_bound + // fn main() { let x: foo!() + bar!() + baz!(); } + + // `foo!() + ...` is invalid syntax for type bounds, + // gracefully exit and let the caller handle the error + if type_marker.kind() == MACRO_TYPE { + return type_marker; + } + // First create a TYPE_BOUND from the completed PATH_TYPE let m = type_marker.precede(p).complete(p, TYPE_BOUND); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 4901ece9cadf3..8104d28bafdf0 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -882,6 +882,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs"); } #[test] + fn macro_as_type_bound() { + run_and_expect_errors("test_data/parser/inline/err/macro_as_type_bound.rs"); + } + #[test] fn macro_rules_as_macro_name() { run_and_expect_errors("test_data/parser/inline/err/macro_rules_as_macro_name.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_as_type_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_as_type_bound.rast new file mode 100644 index 0000000000000..df223efbe9f7c --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_as_type_bound.rast @@ -0,0 +1,67 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "main" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LET_STMT + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "x" + COLON ":" + WHITESPACE " " + MACRO_TYPE + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + BANG "!" + TOKEN_TREE + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + ERROR + PLUS "+" + WHITESPACE " " + EXPR_STMT + BIN_EXPR + MACRO_EXPR + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "bar" + BANG "!" + TOKEN_TREE + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + MACRO_EXPR + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "baz" + BANG "!" + TOKEN_TREE + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" +error 25: expected SEMICOLON +error 26: expected expression, item or let statement diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_as_type_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_as_type_bound.rs new file mode 100644 index 0000000000000..67094ff41435e --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_as_type_bound.rs @@ -0,0 +1 @@ +fn main() { let x: foo!() + bar!() + baz!(); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast index 4722beb61928a..89128b21e83d1 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast @@ -74,39 +74,39 @@ SOURCE_FILE GENERIC_ARG_LIST L_ANGLE "<" TYPE_ARG - DYN_TRAIT_TYPE - TYPE_BOUND_LIST - TYPE_BOUND - MACRO_TYPE - MACRO_CALL - PATH - PATH_SEGMENT - NAME_REF - IDENT "T" - BANG "!" - WHITESPACE " " - PLUS "+" - WHITESPACE " " - TYPE_BOUND - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "T" - ERROR - BANG "!" - TOKEN_TREE - L_CURLY "{" - R_CURLY "}" - R_ANGLE ">" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - R_CURLY "}" + MACRO_TYPE + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + BANG "!" + WHITESPACE " " + ERROR + PLUS "+" + WHITESPACE " " + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + BANG "!" + TOKEN_TREE + L_CURLY "{" + R_CURLY "}" + ERROR + R_ANGLE ">" + WHITESPACE " " + ERROR + L_CURLY "{" + R_CURLY "}" WHITESPACE "\n" error 12: unexpected `!` in type path, macro calls are not allowed here error 21: unexpected `!` in type path, macro calls are not allowed here error 28: unexpected `!` in type path, macro calls are not allowed here error 43: expected `{`, `[`, `(` -error 48: unexpected `!` in type path, macro calls are not allowed here +error 43: expected R_ANGLE +error 43: expected a block +error 44: expected an item +error 50: expected an item +error 52: expected an item diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs index 810737a1dfd14..9f7459066d908 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs +++ b/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs @@ -6,11 +6,10 @@ use proc_macro::TokenStream; use proc_macro2::Span; use queries::{Queries, TrackedQuery, Transparent}; use quote::{ToTokens, format_ident, quote}; -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; +use syn::parse::ParseStream; use syn::spanned::Spanned; use syn::visit_mut::VisitMut; -use syn::{Attribute, FnArg, ItemTrait, Path, Token, TraitItem, parse_quote, parse_quote_spanned}; +use syn::{Attribute, FnArg, ItemTrait, Path, TraitItem, parse_quote, parse_quote_spanned}; mod queries; @@ -87,50 +86,6 @@ enum QueryKind { Transparent, } -#[derive(Default, Debug, Clone)] -struct Cycle { - cycle_result: Option<(syn::Ident, Path)>, -} - -impl Parse for Cycle { - fn parse(input: ParseStream<'_>) -> syn::Result { - let options = Punctuated::::parse_terminated(input)?; - let mut cycle_result = None; - for option in options { - let name = option.name.to_string(); - match &*name { - "cycle_result" => { - if cycle_result.is_some() { - return Err(syn::Error::new_spanned(&option.name, "duplicate option")); - } - cycle_result = Some((option.name, option.value)); - } - _ => { - return Err(syn::Error::new_spanned( - &option.name, - "unknown cycle option. Accepted values: `cycle_result`", - )); - } - } - } - return Ok(Self { cycle_result }); - - struct Option { - name: syn::Ident, - value: Path, - } - - impl Parse for Option { - fn parse(input: ParseStream<'_>) -> syn::Result { - let name = input.parse()?; - input.parse::()?; - let value = input.parse()?; - Ok(Self { name, value }) - } - } - } -} - pub(crate) fn query_group_impl( _args: proc_macro::TokenStream, input: proc_macro::TokenStream, @@ -158,7 +113,6 @@ pub(crate) fn query_group_impl( let mut query_kind = QueryKind::TrackedWithSalsaStruct; let mut invoke = None; - let mut cycle = None; let params: Vec = signature.inputs.clone().into_iter().collect(); let pat_and_tys = params @@ -172,10 +126,6 @@ pub(crate) fn query_group_impl( for SalsaAttr { name, tts, span } in salsa_attrs { match name.as_str() { - "cycle" => { - let c = syn::parse::>(tts)?; - cycle = Some(c.0); - } "invoke" => { let path = syn::parse::>(tts)?; invoke = Some(path.0.clone()); @@ -208,7 +158,6 @@ pub(crate) fn query_group_impl( signature: signature.clone(), pat_and_tys: pat_and_tys.clone(), invoke, - cycle, default: method.default.take(), }; diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs index 96e8ca5758f6e..935d65bb24581 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs +++ b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs @@ -3,15 +3,12 @@ use quote::{ToTokens, format_ident, quote, quote_spanned}; use syn::{Ident, PatType, Path, spanned::Spanned}; -use crate::Cycle; - pub(crate) struct TrackedQuery { pub(crate) trait_name: Ident, pub(crate) signature: syn::Signature, pub(crate) pat_and_tys: Vec, pub(crate) invoke: Option, pub(crate) default: Option, - pub(crate) cycle: Option, } impl ToTokens for TrackedQuery { @@ -29,15 +26,6 @@ impl ToTokens for TrackedQuery { let fn_ident = &sig.ident; let shim: Ident = format_ident!("{}_shim", fn_ident); - let options = self - .cycle - .as_ref() - .map(|Cycle { cycle_result }| { - cycle_result.as_ref().map(|(ident, path)| quote!(#ident=#path)) - }) - .into_iter(); - let annotation = quote!(#[salsa_macros::tracked( #(#options),* )]); - let pat_and_tys = &self.pat_and_tys; let params = self .pat_and_tys @@ -55,7 +43,7 @@ impl ToTokens for TrackedQuery { let method = quote! { #sig { - #annotation + #[salsa_macros::tracked] fn #shim<'db>( db: &'db dyn #trait_name, #(#pat_and_tys),* diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index f175bb06bf679..e05385528a052 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -14,14 +14,8 @@ use std::{env, fs, path::PathBuf, process::ExitCode, sync::Arc}; use anyhow::Context; use lsp_server::Connection; -use paths::Utf8PathBuf; -use rust_analyzer::{ - cli::flags, - config::{Config, ConfigChange, ConfigErrors}, - from_json, -}; +use rust_analyzer::{cli::flags, config::Config}; use tracing_subscriber::fmt::writer::BoxMakeWriter; -use vfs::AbsPathBuf; #[cfg(feature = "mimalloc")] #[global_allocator] @@ -71,7 +65,7 @@ fn actual_main() -> anyhow::Result { with_extra_thread( "LspServer", stdx::thread::ThreadIntent::LatencySensitive, - run_server, + move || run_server(None), )?; } flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, @@ -190,171 +184,17 @@ fn with_extra_thread( Ok(()) } -fn run_server() -> anyhow::Result<()> { - tracing::info!("server version {} will start", rust_analyzer::version()); - +fn run_server(startup_notice: Option) -> anyhow::Result<()> { let (connection, io_threads) = Connection::stdio(); - let (initialize_id, initialize_params) = match connection.initialize_start() { - Ok(it) => it, - Err(e) => { - if e.channel_is_disconnected() { - io_threads.join()?; - } - return Err(e.into()); - } - }; - - tracing::info!("InitializeParams: {}", initialize_params); - let lsp_types::InitializeParams { - #[expect(deprecated, reason = "compatibility with old clients")] - root_uri, - capabilities, - workspace_folders_initialize_params, - initialization_options, - client_info, - .. - } = from_json::("InitializeParams", &initialize_params)?; - - let root_path = match root_uri - .and_then(|it| it.to_file_path().ok()) - .map(patch_path_prefix) - .and_then(|it| Utf8PathBuf::from_path_buf(it).ok()) - .and_then(|it| AbsPathBuf::try_from(it).ok()) - { - Some(it) => it, - None => { - let cwd = env::current_dir()?; - AbsPathBuf::assert_utf8(cwd) - } - }; - - if let Some(client_info) = &client_info { - tracing::info!( - "Client '{}' {}", - client_info.name, - client_info.version.as_deref().unwrap_or_default() - ); - } - - let workspace_roots = workspace_folders_initialize_params - .workspace_folders - .and_then(|workspaces| match workspaces { - lsp_types::WorkspaceFolders::WorkspaceFolderList(workspace_folders) => { - Some(workspace_folders) - } - lsp_types::WorkspaceFolders::Null => None, - }) - .map(|workspaces| { - workspaces - .into_iter() - .filter_map(|it| it.uri.to_file_path().ok()) - .map(patch_path_prefix) - .filter_map(|it| Utf8PathBuf::from_path_buf(it).ok()) - .filter_map(|it| AbsPathBuf::try_from(it).ok()) - .collect::>() - }) - .filter(|workspaces| !workspaces.is_empty()) - .unwrap_or_else(|| vec![root_path.clone()]); - let mut config = Config::new(root_path, capabilities, workspace_roots, client_info); - if let Some(json) = initialization_options { - let mut change = ConfigChange::default(); - change.change_client_config(json); - - let error_sink: ConfigErrors; - (config, error_sink, _) = config.apply_change(change); - - if !error_sink.is_empty() { - use lsp_types::{ - MessageType, Notification as _, ShowMessageNotification, ShowMessageParams, - }; - let not = lsp_server::Notification::new( - ShowMessageNotification::METHOD.into(), - ShowMessageParams { kind: MessageType::Warning, message: error_sink.to_string() }, - ); - connection.sender.send(lsp_server::Message::Notification(not)).unwrap(); - } - } - - let server_capabilities = rust_analyzer::server_capabilities(&config); - - let initialize_result = lsp_types::InitializeResult { - capabilities: server_capabilities, - server_info: Some(lsp_types::ServerInfo { - name: String::from("rust-analyzer"), - version: Some(rust_analyzer::version().to_string()), - }), - }; - - let initialize_result = serde_json::to_value(initialize_result).unwrap(); - - if let Err(e) = connection.initialize_finish(initialize_id, initialize_result) { - if e.channel_is_disconnected() { - io_threads.join()?; - } - return Err(e.into()); - } - - if config.discover_workspace_config().is_none() - && !config.has_linked_projects() - && config.detached_files().is_empty() - { - config.rediscover_workspaces(); - } - rayon::ThreadPoolBuilder::new() .thread_name(|ix| format!("RayonWorker{}", ix)) .build_global() .unwrap(); - // If the io_threads have an error, there's usually an error on the main - // loop too because the channels are closed. Ensure we report both errors. - match (rust_analyzer::main_loop(config, connection), io_threads.join()) { - (Err(loop_e), Err(join_e)) => anyhow::bail!("{loop_e}\n{join_e}"), - (Ok(_), Err(join_e)) => anyhow::bail!("{join_e}"), - (Err(loop_e), Ok(_)) => anyhow::bail!("{loop_e}"), - (Ok(_), Ok(_)) => {} - } - - tracing::info!("server did shut down"); - Ok(()) -} - -fn patch_path_prefix(path: PathBuf) -> PathBuf { - use std::path::{Component, Prefix}; - if cfg!(windows) { - // VSCode might report paths with the file drive in lowercase, but this can mess - // with env vars set by tools and build scripts executed by r-a such that it invalidates - // cargo's compilations unnecessarily. https://github.com/rust-lang/rust-analyzer/issues/14683 - // So we just uppercase the drive letter here unconditionally. - // (doing it conditionally is a pain because std::path::Prefix always reports uppercase letters on windows) - let mut comps = path.components(); - match comps.next() { - Some(Component::Prefix(prefix)) => { - let prefix = match prefix.kind() { - Prefix::Disk(d) => { - format!("{}:", d.to_ascii_uppercase() as char) - } - Prefix::VerbatimDisk(d) => { - format!(r"\\?\{}:", d.to_ascii_uppercase() as char) - } - _ => return path, - }; - let mut path = PathBuf::new(); - path.push(prefix); - path.extend(comps); - path - } - _ => path, - } - } else { - path - } -} - -#[test] -#[cfg(windows)] -fn patch_path_prefix_works() { - assert_eq!(patch_path_prefix(r"c:\foo\bar".into()), PathBuf::from(r"C:\foo\bar")); - assert_eq!(patch_path_prefix(r"\\?\c:\foo\bar".into()), PathBuf::from(r"\\?\C:\foo\bar")); + rust_analyzer::session::run_session( + connection, + rust_analyzer::session::IoThreads::Stdio(io_threads), + startup_notice, + ) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index f464a2487507e..fcb34b743adbd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -216,7 +216,7 @@ config_data! { /// to always show them). inlayHints_closingBraceHints_minLines: usize = 25, - /// Show inlay hints for closure captures. + /// Show inlay hints for closure and coroutine captures. inlayHints_closureCaptureHints_enable: bool = false, /// Show inlay type hints for return types of closures. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index d392c1ecbf390..47a98476cd83f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -84,8 +84,12 @@ pub(crate) fn handle_did_open_text_document( return Ok(()); } - let contents = params.text_document.text.into_bytes(); - state.vfs.write().0.set_file_contents(path, Some(contents)); + // Library files are immutable: the client never becomes authoritative over their + // contents, disk is the truth. + if !state.source_root_config.path_is_library(&path) { + let contents = params.text_document.text.into_bytes(); + state.vfs.write().0.set_file_contents(path, Some(contents)); + } if state.config.discover_workspace_config().is_some() { tracing::debug!("queuing task"); let _ = state @@ -120,7 +124,10 @@ pub(crate) fn handle_did_change_text_document( .into_bytes(); if *data != new_contents { data.clone_from(&new_contents); - state.vfs.write().0.set_file_contents(path, Some(new_contents)); + // Library files are immutable, changes to them are ignored. + if !state.source_root_config.path_is_library(&path) { + state.vfs.write().0.set_file_contents(path, Some(new_contents)); + } } } Ok(()) @@ -156,6 +163,14 @@ pub(crate) fn handle_did_save_text_document( params: DidSaveTextDocumentParams, ) -> anyhow::Result<()> { if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { + // Library files are immutable and not watched, so the save is the only chance to + // pick up the changed disk contents. + if state.source_root_config.path_is_library(&vfs_path) + && let Some(path) = vfs_path.as_path() + { + state.loader.handle.invalidate(path.to_path_buf()); + } + let snap = state.snapshot(); let file_id = try_default!(snap.vfs_path_to_file_id(&vfs_path)?); let sr = snap.analysis.source_root_id(file_id)?; @@ -331,12 +346,10 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { // have this problem. Remove the line below when triomphe::Arc has an UnwindSafe impl // like std::sync::Arc's. let world = world; - stdx::always!( - world.flycheck.len() == 1, - "should have exactly one flycheck handle when invocation strategy is once" - ); let saved_file = vfs_path.as_path().map(ToOwned::to_owned); - world.flycheck[0].restart_workspace(saved_file); + if let Some(flycheck) = world.flycheck.first() { + flycheck.restart_workspace(saved_file); + } Ok(()) }) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 950592cb26455..e953ea2136297 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -60,6 +60,7 @@ pub mod tracing { pub mod config; mod global_state; pub mod lsp; +pub mod session; use self::lsp::ext as lsp_ext; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index b4727360e5375..c73be92a90906 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -829,6 +829,7 @@ impl GlobalState { health @ (lsp_ext::Health::Warning | lsp_ext::Health::Error), Some(message), ) = (status.health, &status.message) + && self.last_reported_status.message != status.message { let open_log_button = tracing::enabled!(tracing::Level::ERROR) && (self.fetch_build_data_error().is_err() @@ -995,10 +996,14 @@ impl GlobalState { } let path = VfsPath::from(path); - // if the file is in mem docs, it's managed by the client via notifications - // so only set it if its not in there - if !self.mem_docs.contains(&path) - && (is_changed || vfs.file_id(&path).is_none()) + // If the file is in mem docs, it's managed by the client via + // notifications so only set it if its not in there. Library files are + // exempt from that authority as they are considered immutable, for + // them disk is always the source of truth. + let is_library = self.source_root_config.path_is_library(&path); + let client_is_authoritative = !is_library && self.mem_docs.contains(&path); + if !client_is_authoritative + && (is_changed || is_library || vfs.file_id(&path).is_none()) { vfs.set_file_contents(path, contents); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/session.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/session.rs new file mode 100644 index 0000000000000..3536f47999037 --- /dev/null +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/session.rs @@ -0,0 +1,212 @@ +//! Entry point of a single LSP session: initialization handshake, then the main loop. + +use std::{env, path::PathBuf}; + +use anyhow::Context; +use lsp_server::Connection; +use paths::Utf8PathBuf; +use vfs::AbsPathBuf; + +use crate::{ + config::{Config, ConfigChange, ConfigErrors}, + from_json, +}; + +/// Handles to the I/O threads shuttling the messages of a [`Connection`], joined when +/// the session ends to surface transport errors. +pub enum IoThreads { + /// The stdio transport of a standalone server. + Stdio(lsp_server::IoThreads), +} + +impl IoThreads { + fn join(self) -> anyhow::Result<()> { + match self { + IoThreads::Stdio(io_threads) => Ok(io_threads.join()?), + } + } +} + +/// Runs a full LSP session over `connection`: waits for the client's `initialize`, +/// negotiates capabilities, then runs the main loop until the client disconnects or +/// requests shutdown. +/// +/// # Errors +/// +/// Returns an error if the connection breaks down or the main loop exits abnormally. +pub fn run_session( + connection: Connection, + io_threads: IoThreads, + startup_notice: Option, +) -> anyhow::Result<()> { + tracing::info!("server version {} will start", crate::version()); + + let (initialize_id, initialize_params) = match connection.initialize_start() { + Ok(it) => it, + Err(e) => { + if e.channel_is_disconnected() { + io_threads.join()?; + } + return Err(e.into()); + } + }; + + tracing::info!("InitializeParams: {}", initialize_params); + let lsp_types::InitializeParams { + #[expect(deprecated, reason = "compatibility with old clients")] + root_uri, + capabilities, + workspace_folders_initialize_params, + initialization_options, + client_info, + .. + } = from_json::("InitializeParams", &initialize_params)?; + + let root_path = match root_uri + .and_then(|it| it.to_file_path().ok()) + .map(patch_path_prefix) + .and_then(|it| Utf8PathBuf::from_path_buf(it).ok()) + .and_then(|it| AbsPathBuf::try_from(it).ok()) + { + Some(it) => it, + None => { + let cwd = env::current_dir().context("couldn't determine working directory")?; + AbsPathBuf::assert_utf8(cwd) + } + }; + + if let Some(client_info) = &client_info { + tracing::info!( + "Client '{}' {}", + client_info.name, + client_info.version.as_deref().unwrap_or_default() + ); + } + + let workspace_roots = workspace_folders_initialize_params + .workspace_folders + .and_then(|workspaces| match workspaces { + lsp_types::WorkspaceFolders::WorkspaceFolderList(workspace_folders) => { + Some(workspace_folders) + } + lsp_types::WorkspaceFolders::Null => None, + }) + .map(|workspaces| { + workspaces + .into_iter() + .filter_map(|it| it.uri.to_file_path().ok()) + .map(patch_path_prefix) + .filter_map(|it| Utf8PathBuf::from_path_buf(it).ok()) + .filter_map(|it| AbsPathBuf::try_from(it).ok()) + .collect::>() + }) + .filter(|workspaces| !workspaces.is_empty()) + .unwrap_or_else(|| vec![root_path.clone()]); + let mut config = Config::new(root_path, capabilities, workspace_roots, client_info); + if let Some(json) = initialization_options { + let mut change = ConfigChange::default(); + change.change_client_config(json); + + let error_sink: ConfigErrors; + (config, error_sink, _) = config.apply_change(change); + + if !error_sink.is_empty() { + use lsp_types::{ + MessageType, Notification as _, ShowMessageNotification, ShowMessageParams, + }; + let not = lsp_server::Notification::new( + ShowMessageNotification::METHOD.into(), + ShowMessageParams { kind: MessageType::Warning, message: error_sink.to_string() }, + ); + connection.sender.send(lsp_server::Message::Notification(not)).unwrap(); + } + } + + let server_capabilities = crate::server_capabilities(&config); + + let initialize_result = lsp_types::InitializeResult { + capabilities: server_capabilities, + server_info: Some(lsp_types::ServerInfo { + name: String::from("rust-analyzer"), + version: Some(crate::version().to_string()), + }), + }; + + let initialize_result = serde_json::to_value(initialize_result).unwrap(); + + if let Err(e) = connection.initialize_finish(initialize_id, initialize_result) { + if e.channel_is_disconnected() { + io_threads.join()?; + } + return Err(e.into()); + } + + if let Some(notice) = startup_notice { + use lsp_types::{ + MessageType, Notification as _, ShowMessageNotification, ShowMessageParams, + }; + let not = lsp_server::Notification::new( + ShowMessageNotification::METHOD.into(), + ShowMessageParams { kind: MessageType::Warning, message: notice }, + ); + connection.sender.send(lsp_server::Message::Notification(not)).unwrap(); + } + + if config.discover_workspace_config().is_none() + && !config.has_linked_projects() + && config.detached_files().is_empty() + { + config.rediscover_workspaces(); + } + + // If the io_threads have an error, there's usually an error on the main + // loop too because the channels are closed. Ensure we report both errors. + match (crate::main_loop(config, connection), io_threads.join()) { + (Err(loop_e), Err(join_e)) => anyhow::bail!("{loop_e}\n{join_e}"), + (Ok(_), Err(join_e)) => anyhow::bail!("{join_e}"), + (Err(loop_e), Ok(_)) => anyhow::bail!("{loop_e}"), + (Ok(_), Ok(_)) => {} + } + + tracing::info!("server did shut down"); + Ok(()) +} + +fn patch_path_prefix(path: PathBuf) -> PathBuf { + use std::path::{Component, Prefix}; + if cfg!(windows) { + // VSCode might report paths with the file drive in lowercase, but this can mess + // with env vars set by tools and build scripts executed by r-a such that it invalidates + // cargo's compilations unnecessarily. https://github.com/rust-lang/rust-analyzer/issues/14683 + // So we just uppercase the drive letter here unconditionally. + // (doing it conditionally is a pain because std::path::Prefix always reports uppercase letters on windows) + let mut comps = path.components(); + match comps.next() { + Some(Component::Prefix(prefix)) => { + let prefix = match prefix.kind() { + Prefix::Disk(d) => { + format!("{}:", d.to_ascii_uppercase() as char) + } + Prefix::VerbatimDisk(d) => { + format!(r"\\?\{}:", d.to_ascii_uppercase() as char) + } + _ => return path, + }; + let mut path = PathBuf::new(); + path.push(prefix); + path.extend(comps); + path + } + _ => path, + } + } else { + path + } +} + +#[test] +#[cfg(windows)] +fn patch_path_prefix_works() { + assert_eq!(patch_path_prefix(r"c:\foo\bar".into()), PathBuf::from(r"C:\foo\bar")); + assert_eq!(patch_path_prefix(r"\\?\c:\foo\bar".into()), PathBuf::from(r"\\?\C:\foo\bar")); +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index b0e59ff159539..9017bae474273 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -565,7 +565,17 @@ pub fn async_move_block_expr( } pub fn tail_only_block_expr(tail_expr: ast::Expr) -> ast::BlockExpr { - ast_from_text(&format!("fn f() {{ {tail_expr} }}")) + quote! { + BlockExpr { + StmtList { + ['{'] + " " + #tail_expr + " " + ['}'] + } + } + } } /// Ideally this function wouldn't exist since it involves manual indenting. @@ -686,7 +696,12 @@ pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr { expr_from_text(&format!("{token}{expr}")) } pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr { - expr_from_text(&format!("{f}{arg_list}")) + quote! { + CallExpr { + #f + #arg_list + } + } } pub fn expr_method_call( receiver: ast::Expr, @@ -1038,7 +1053,14 @@ pub fn untyped_param(pat: ast::Pat) -> ast::Param { } pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { - ast_from_text(&format!("fn f({pat}: {ty}) {{ }}")) + quote! { + Param { + #pat + [:] + " " + #ty + } + } } pub fn self_param() -> ast::SelfParam { diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 0588b1c77d1c1..f3a460b9ac3da 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -59,6 +59,7 @@ //! option: panic //! ord: eq, option //! panic: fmt +//! panic_location: panic //! pat: panic //! phantom_data: //! pin: @@ -2093,7 +2094,38 @@ pub mod str { // endregion:str // region:panic -mod panic { +pub mod panic { + // region:panic_location + #[rustc_intrinsic] + pub const fn caller_location() -> &'static Location<'static>; + + #[lang = "panic_location"] + pub struct Location<'a> { + file: &'a str, + line: u32, + col: u32, + } + + impl<'a> Location<'a> { + #[track_caller] + pub const fn caller() -> &'static Location<'static> { + caller_location() + } + + pub const fn file(&self) -> &str { + self.file + } + + pub const fn line(&self) -> u32 { + self.line + } + + pub const fn column(&self) -> u32 { + self.col + } + } + // endregion:panic_location + pub macro panic_2021 { () => ({ const fn panic_cold_explicit() -> ! { @@ -2483,6 +2515,7 @@ macro_rules! matches { pub mod prelude { pub mod v1 { + #[rustfmt::skip] pub use crate::{ clone::Clone, // :clone cmp::{Eq, PartialEq}, // :eq @@ -2509,6 +2542,16 @@ pub mod prelude { panic, // :panic result::Result::{self, Err, Ok}, // :result str::FromStr, // :str + write, writeln, // :write + assert, // :assert + format_args, format_args_nl, const_format_args, print, // :fmt + todo, // :todo + unimplemented, // :unimplemented + include, // :include + include_bytes, // :include_bytes + concat, // :concat + env, option_env, // :env + matches, // :matches }; } diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs index 0c41ede5b53aa..c25cda2d36d29 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs @@ -128,6 +128,14 @@ impl FileSetConfig { self.map.stream().into_byte_vec() } + /// Returns the index of the set `path` would be partitioned into, or `None` if it + /// belongs to none of the configured sets (that is, the catch-all set for everything + /// else). + pub fn classify_path(&self, path: &VfsPath) -> Option { + let idx = self.classify(path, &mut Vec::new()); + (idx != self.len() - 1).then_some(idx) + } + /// Returns the set index for the given `path`. /// /// `scratch_space` is used as a buffer and will be entirely replaced. diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs index 3cdb60dcb260e..24b7438d5f37c 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs @@ -41,6 +41,26 @@ fn name_prefix() { assert_eq!(partition, vec![1, 1, 0]); } +#[test] +fn classify_path() { + let mut file_set = FileSetConfig::builder(); + file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]); + file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar/baz".into())]); + let file_set = file_set.build(); + + let classify = |path: &str| file_set.classify_path(&VfsPath::new_virtual_path(path.into())); + assert_eq!(classify("/foo/src/lib.rs"), Some(0)); + assert_eq!(classify("/foo/bar/baz/lib.rs"), Some(1)); + assert_eq!(classify("/quux/lib.rs"), None); +} + +#[test] +fn classify_path_default_config() { + let file_set = FileSetConfig::default(); + let path = VfsPath::new_virtual_path("/foo/lib.rs".into()); + assert_eq!(file_set.classify_path(&path), None); +} + /// Ensure that we don't consider `/foo/bar_baz.rs` to be in the /// `/foo/bar/` root. #[test] diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 9d865a7936a95..fd377616d9566 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -991,7 +991,7 @@ to always show them). Default: `false` -Show inlay hints for closure captures. +Show inlay hints for closure and coroutine captures. ## rust-analyzer.inlayHints.closureReturnTypeHints.enable {#inlayHints.closureReturnTypeHints.enable} diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 92279a8c0d69c..61bc4cb29dfb7 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2237,7 +2237,7 @@ "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.closureCaptureHints.enable": { - "markdownDescription": "Show inlay hints for closure captures.", + "markdownDescription": "Show inlay hints for closure and coroutine captures.", "default": false, "type": "boolean" } diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 06a452984fc32..1a179ef0812a7 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.9.0" +version = "0.10.0" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs b/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs index eb0832745560a..7efe063551697 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs @@ -82,9 +82,7 @@ use toolchain::command; // clippy-approved wrapper #[allow(clippy::print_stderr, clippy::disallowed_types, clippy::disallowed_methods)] use anyhow::{Context, Result, anyhow, bail}; -use lsp_server::{ - Connection, Message, Request as ServerRequest, RequestId, Response, ResponseKind, -}; +use lsp_server::{Connection, Message, Request as ServerRequest, RequestId, Response}; // ===================================================================== // main @@ -306,8 +304,7 @@ fn full_range(text: &str) -> Range { } fn send_ok(conn: &Connection, id: RequestId, result: &T) -> Result<()> { - let resp = - Response { id, response_kind: ResponseKind::Ok { result: serde_json::to_value(result)? } }; + let resp = Response { id, response_result: Ok(serde_json::to_value(result)?) }; conn.sender.send(Message::Response(resp))?; Ok(()) } @@ -320,9 +317,11 @@ fn send_err( ) -> Result<()> { let resp = Response { id, - response_kind: ResponseKind::Err { - error: lsp_server::ResponseError { code: code as i32, message: msg.into(), data: None }, - }, + response_result: Err(lsp_server::ResponseError { + code: code as i32, + message: msg.into(), + data: None, + }), }; conn.sender.send(Message::Response(resp))?; Ok(()) diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs index 5eaedbb6429d2..3f29c39c49f09 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs @@ -22,9 +22,7 @@ use crossbeam_channel::{Receiver, RecvError, RecvTimeoutError, Sender}; pub use crate::{ error::{ExtractError, ProtocolError}, - msg::{ - ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError, ResponseKind, - }, + msg::{ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError}, req_queue::{Incoming, Outgoing, ReqQueue}, stdio::IoThreads, }; diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 64e2ba7509047..19fc106134ef5 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -84,15 +84,17 @@ pub struct Response { // request id. We fail deserialization in that case, so we just // make this field mandatory. pub id: RequestId, - #[serde(flatten)] - pub response_kind: ResponseKind, + #[serde(flatten, with = "ResponseResult")] + pub response_result: Result, } -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(untagged)] -pub enum ResponseKind { - Ok { result: serde_json::Value }, - Err { error: ResponseError }, +#[derive(Serialize, Deserialize)] +#[serde(remote = "Result")] +enum ResponseResult { + #[serde(rename = "result")] + Ok(serde_json::Value), + #[serde(rename = "error")] + Err(ResponseError), } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -203,14 +205,11 @@ impl Message { impl Response { pub fn new_ok(id: RequestId, result: R) -> Response { - Response { - id, - response_kind: ResponseKind::Ok { result: serde_json::to_value(result).unwrap() }, - } + Response { id, response_result: Ok(serde_json::to_value(result).unwrap()) } } pub fn new_err(id: RequestId, code: i32, message: String) -> Response { let error = ResponseError { code, message, data: None }; - Response { id, response_kind: ResponseKind::Err { error } } + Response { id, response_result: Err(error) } } } @@ -305,11 +304,11 @@ fn write_msg_text(out: &mut dyn Write, msg: &str) -> io::Result<()> { #[cfg(test)] mod tests { - use super::{Message, Notification, Request, RequestId}; + use super::{Message, Notification, Request, RequestId, Response}; #[test] fn shutdown_with_explicit_null() { - let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\", \"params\": null }"; + let text = r#"{"jsonrpc": "2.0","id": 3,"method": "shutdown", "params": null }"#; let msg: Message = serde_json::from_str(text).unwrap(); assert!( @@ -319,7 +318,7 @@ mod tests { #[test] fn shutdown_with_no_params() { - let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\"}"; + let text = r#"{"jsonrpc": "2.0","id": 3,"method": "shutdown"}"#; let msg: Message = serde_json::from_str(text).unwrap(); assert!( @@ -329,7 +328,7 @@ mod tests { #[test] fn notification_with_explicit_null() { - let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\", \"params\": null }"; + let text = r#"{"jsonrpc": "2.0","method": "exit", "params": null }"#; let msg: Message = serde_json::from_str(text).unwrap(); assert!(matches!(msg, Message::Notification(not) if not.method == "exit")); @@ -337,7 +336,7 @@ mod tests { #[test] fn notification_with_no_params() { - let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\"}"; + let text = r#"{"jsonrpc": "2.0","method": "exit"}"#; let msg: Message = serde_json::from_str(text).unwrap(); assert!(matches!(msg, Message::Notification(not) if not.method == "exit")); @@ -352,7 +351,7 @@ mod tests { }); let serialized = serde_json::to_string(&msg).unwrap(); - assert_eq!("{\"id\":3,\"method\":\"shutdown\"}", serialized); + assert_eq!(r#"{"id":3,"method":"shutdown"}"#, serialized); } #[test] @@ -363,6 +362,29 @@ mod tests { }); let serialized = serde_json::to_string(&msg).unwrap(); - assert_eq!("{\"method\":\"exit\"}", serialized); + assert_eq!(r#"{"method":"exit"}"#, serialized); + } + + #[test] + fn serialize_ok_response() { + let msg = Message::Response(Response::new_ok(RequestId::from(3), "success")); + let serialized = serde_json::to_string(&msg).unwrap(); + + assert_eq!(r#"{"id":3,"result":"success"}"#, serialized); + } + + #[test] + fn serialize_err_response() { + let msg = Message::Response(Response::new_err( + RequestId::from(3), + -32600, + String::from("bad response message"), + )); + let serialized = serde_json::to_string(&msg).unwrap(); + + assert_eq!( + r#"{"id":3,"error":{"code":-32600,"message":"bad response message"}}"#, + serialized + ); } } diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs b/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs index 0c39a1dc7a1f7..52493d159854a 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{ErrorCode, Request, RequestId, Response, ResponseError, msg::ResponseKind}; +use crate::{ErrorCode, Request, RequestId, Response, ResponseError}; /// Manages the set of pending requests, both incoming and outgoing. #[derive(Debug)] @@ -47,7 +47,7 @@ impl Incoming { message: "canceled by client".to_owned(), data: None, }; - Some(Response { id, response_kind: ResponseKind::Err { error } }) + Some(Response { id, response_result: Err(error) }) } pub fn complete(&mut self, id: &RequestId) -> Option { diff --git a/src/tools/rust-analyzer/lib/ungrammar/README.md b/src/tools/rust-analyzer/lib/ungrammar/README.md index a5e130fedf102..7f6eb98079622 100644 --- a/src/tools/rust-analyzer/lib/ungrammar/README.md +++ b/src/tools/rust-analyzer/lib/ungrammar/README.md @@ -4,7 +4,7 @@ A DSL for specifying concrete syntax trees. See the [blog post][post] for an introduction. -See [./rust.ungram](./rust.ungram) for an example. +See [./ungrammar.ungram](./ungrammar.ungram) for an example. ## Editor support diff --git a/src/tools/rust-analyzer/xtask/src/install.rs b/src/tools/rust-analyzer/xtask/src/install.rs index bbb6d9aeac25e..53617c65631ef 100644 --- a/src/tools/rust-analyzer/xtask/src/install.rs +++ b/src/tools/rust-analyzer/xtask/src/install.rs @@ -108,12 +108,8 @@ fn install_client(sh: &Shell, client_opt: ClientOpt) -> anyhow::Result<()> { }; // Find the appropriate VS Code binary. - let lifetime_extender; let candidates: &[&str] = match client_opt.code_bin.as_deref() { - Some(it) => { - lifetime_extender = [it]; - &lifetime_extender[..] - } + Some(it) => &[it], None => VS_CODES, }; let code = candidates