Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22673 from ChayimFriedman2/async-fn-param-ide
fix: Fix handling of params of coroutine fns
| -rw-r--r-- | crates/hir-def/src/expr_store/body.rs | 42 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 194 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/pretty.rs | 6 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/scope.rs | 12 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/tests/body.rs | 4 | ||||
| -rw-r--r-- | crates/hir-ty/src/diagnostics/unsafe_check.rs | 8 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 15 | ||||
| -rw-r--r-- | crates/hir-ty/src/mir/lower.rs | 8 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests.rs | 3 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/closure_captures.rs | 2 | ||||
| -rw-r--r-- | crates/hir/src/display.rs | 2 | ||||
| -rw-r--r-- | crates/hir/src/lib.rs | 10 | ||||
| -rw-r--r-- | crates/hir/src/semantics/source_to_def.rs | 2 | ||||
| -rw-r--r-- | crates/hir/src/source_analyzer.rs | 2 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/inline_call.rs | 19 | ||||
| -rw-r--r-- | crates/ide/src/highlight_related.rs | 22 | ||||
| -rw-r--r-- | crates/ide/src/syntax_highlighting/test_data/async_fn_non_mut_param.html | 46 | ||||
| -rw-r--r-- | crates/ide/src/syntax_highlighting/tests.rs | 13 |
18 files changed, 255 insertions, 155 deletions
diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs index 01423d5109..5b254c5711 100644 --- a/crates/hir-def/src/expr_store/body.rs +++ b/crates/hir-def/src/expr_store/body.rs @@ -2,7 +2,6 @@ //! consts. use std::ops; -use arrayvec::ArrayVec; use hir_expand::{InFile, Lookup}; use span::Edition; use syntax::ast; @@ -18,6 +17,24 @@ use crate::{ src::HasSource, }; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Param<Id> { + /// The id of the formal parameter. In analysis, you want to use this: it has a special status as a parameter, + /// while [`user_written`][Self::user_written] is just a local variable. + pub formal: Id, + /// The id that corresponds to the pattern the user wrote. In IDE, you usually want to use this. + /// + /// It will be different than [`formal`][Self::formal] for coroutine fns (async fn etc.), because we apply + /// a desugaring for those that copies the parameter into a local variable. + pub user_written: Id, +} + +impl<Id: Copy> Param<Id> { + pub(crate) fn new(id: Id) -> Self { + Self { formal: id, user_written: id } + } +} + /// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq)] pub struct Body { @@ -28,13 +45,8 @@ pub struct Body { /// /// If this `Body` is for the body of a constant, this will just be /// empty. - pub params: Box<[PatId]>, - /// The first element, if it exists, is the real `self` binding. - /// - /// The second element is used for `async fn` (or `gen fn` etc.). These functions - /// have to put a `let self = self` inside the returned coroutine, and the second element - /// points at it. - pub self_params: ArrayVec<BindingId, 2>, + pub params: Box<[Param<PatId>]>, + pub self_param: Option<Param<BindingId>>, } impl ops::Deref for Body { @@ -128,14 +140,12 @@ impl Body { self.store.expr_roots().next_back().unwrap() } - pub fn self_param(&self) -> Option<BindingId> { - self.self_params.first().copied() - } - - /// `async fn` (or `gen fn` etc.), have to put a `let self = self` inside the returned coroutine. - /// This function returns it. - pub fn coroutine_self_binding(&self) -> Option<BindingId> { - self.self_params.get(1).copied() + /// Returns `true` if this is the formal or user-written self param. + #[inline] + pub fn is_any_self_param(&self, binding: BindingId) -> bool { + self.self_param.is_some_and(|self_param| { + self_param.formal == binding || self_param.user_written == binding + }) } pub fn pretty_print( diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index afbcf4cc84..f2523390cd 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -8,7 +8,6 @@ mod path; use std::{cell::OnceCell, mem}; -use arrayvec::ArrayVec; use base_db::FxIndexSet; use cfg::CfgOptions; use either::Either; @@ -19,6 +18,7 @@ use hir_expand::{ span_map::SpanMap, }; use intern::{Symbol, sym}; +use itertools::Itertools; use rustc_abi::ExternAbi; use rustc_hash::FxHashMap; use smallvec::SmallVec; @@ -43,6 +43,7 @@ use crate::{ Body, BodySourceMap, ExprPtr, ExprRoot, ExpressionStore, ExpressionStoreBuilder, ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, LifetimePtr, PatPtr, TypePtr, + body::Param, expander::Expander, lower::generics::ImplTraitLowerFn, path::{AssociatedTypeBinding, GenericArg, GenericArgs, GenericArgsParentheses, Path}, @@ -82,7 +83,7 @@ pub(super) fn lower_body( // even though they should be the same. Also, when the body comes from multiple expansions, their // hygiene is different. - let mut self_params = ArrayVec::new(); + 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); @@ -100,9 +101,43 @@ pub(super) fn lower_body( // If #[rust_analyzer::skip] annotated, only construct enough information for the signature // and skip the body. if skip_body { + collector.with_expr_root(|collector| { + if let Some(param_list) = parameters { + if let Some(self_param_syn) = + param_list.self_param().filter(|self_param| collector.check_cfg(self_param)) + { + let is_mutable = self_param_syn.mut_token().is_some() + && self_param_syn.amp_token().is_none(); + let hygiene = self_param_syn + .name() + .map(|name| collector.hygiene_id_for(name.syntax().text_range())) + .unwrap_or(HygieneId::ROOT); + let binding_id: la_arena::Idx<Binding> = collector.alloc_binding( + Name::new_symbol_root(sym::self_), + BindingAnnotation::new(is_mutable, false), + hygiene, + ); + self_param = Some(Param::new(binding_id)); + source_map_self_param = + Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); + } + let count = param_list.params().filter(|it| collector.check_cfg(it)).count(); + params = (0..count).map(|_| Param::new(collector.missing_pat())).collect(); + }; + + collector.missing_expr() + }); + let (store, source_map) = collector.store.finish(); + return ( + Body { store, params: params.into_boxed_slice(), self_param }, + BodySourceMap { self_param: source_map_self_param, store: source_map }, + ); + } + + collector.with_expr_root(|collector| { if let Some(param_list) = parameters { if let Some(self_param_syn) = - param_list.self_param().filter(|self_param| collector.check_cfg(self_param)) + param_list.self_param().filter(|it| collector.check_cfg(it)) { let is_mutable = self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none(); @@ -115,59 +150,31 @@ pub(super) fn lower_body( BindingAnnotation::new(is_mutable, false), hygiene, ); - self_params.push(binding_id); + self_param = Some(Param::new(binding_id)); source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); } - let count = param_list.params().filter(|it| collector.check_cfg(it)).count(); - params = (0..count).map(|_| collector.missing_pat()).collect(); - }; - collector.with_expr_root(|collector| collector.missing_expr()); - let (store, source_map) = collector.store.finish(); - return ( - Body { store, params: params.into_boxed_slice(), self_params }, - BodySourceMap { self_param: source_map_self_param, store: source_map }, - ); - } - if let Some(param_list) = parameters { - if let Some(self_param_syn) = param_list.self_param().filter(|it| collector.check_cfg(it)) { - let is_mutable = - self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none(); - let hygiene = self_param_syn - .name() - .map(|name| collector.hygiene_id_for(name.syntax().text_range())) - .unwrap_or(HygieneId::ROOT); - let binding_id: la_arena::Idx<Binding> = collector.alloc_binding( - Name::new_symbol_root(sym::self_), - BindingAnnotation::new(is_mutable, false), - hygiene, + let is_extern = matches!( + owner, + DefWithBodyId::FunctionId(id) + if matches!(id.loc(db).container, ItemContainerId::ExternBlockId(_)), ); - self_params.push(binding_id); - source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); - } - let is_extern = matches!( - owner, - DefWithBodyId::FunctionId(id) - if matches!(id.loc(db).container, ItemContainerId::ExternBlockId(_)), - ); - - for param in param_list.params() { - if collector.check_cfg(¶m) { - let param_pat = if is_extern { - collector.collect_extern_fn_param(param.pat()) - } else { - collector.collect_pat_top(param.pat()) - }; - params.push(param_pat); + for param in param_list.params() { + if collector.check_cfg(¶m) { + let param_pat = if is_extern { + collector.collect_extern_fn_param(param.pat()) + } else { + collector.collect_pat_top(param.pat()) + }; + params.push(Param::new(param_pat)); + } } - } - }; + }; - collector.with_expr_root(|collector| { collector.collect( - &mut self_params, + &mut self_param, &mut params, body, if is_async_fn { @@ -187,7 +194,7 @@ pub(super) fn lower_body( let (store, source_map) = collector.store.finish(); ( - Body { store, params: params.into_boxed_slice(), self_params }, + Body { store, params: params.into_boxed_slice(), self_param }, BodySourceMap { self_param: source_map_self_param, store: source_map }, ) } @@ -952,8 +959,8 @@ impl<'db> ExprCollector<'db> { /// drop order are stable. fn lower_coroutine_body_with_moved_arguments( &mut self, - self_params: &mut ArrayVec<BindingId, 2>, - params: &mut [PatId], + self_param: &mut Option<Param<BindingId>>, + params: &mut [Param<PatId>], body: ExprId, kind: CoroutineKind, coroutine_source: CoroutineSource, @@ -984,10 +991,11 @@ impl<'db> ExprCollector<'db> { // If `<pattern>` is a simple ident, then it is lowered to a single // `let <pattern> = <pattern>;` statement as an optimization. - let mut statements = Vec::new(); + let mut statements = + Vec::with_capacity(usize::from(self_param.is_some()) + (params.len() * 2)); - if let Some(&self_param) = self_params.first() { - let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param]; + if let Some(self_param) = self_param { + let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param.formal]; let name = name.clone(); let child_binding_id = self.alloc_binding(name.clone(), mode, hygiene); let child_pat_id = @@ -1003,11 +1011,11 @@ impl<'db> ExprCollector<'db> { initializer: Some(expr), else_branch: None, }); - self_params.push(child_binding_id); + self_param.user_written = child_binding_id; } for param in params { - let (name, hygiene, is_simple_parameter) = match self.store.pats[*param] { + let (name, hygiene, is_simple_parameter) = match self.store.pats[param.formal] { // Check if this is a binding pattern, if so, we can optimize and avoid adding a // `let <pat> = __argN;` statement. In this case, we do not rename the parameter. Pat::Bind { id, subpat: None, .. } @@ -1016,55 +1024,57 @@ impl<'db> ExprCollector<'db> { BindingAnnotation::Unannotated | BindingAnnotation::Mutable ) => { - (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, true) + let binding = &self.store.bindings[id]; + (binding.name.clone(), binding.hygiene, true) } Pat::Bind { id, .. } => { // If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display. - (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, false) + let binding = &self.store.bindings[id]; + (binding.name.clone(), binding.hygiene, false) } _ => (self.generate_new_name(), HygieneId::ROOT, false), }; - let pat_syntax = self.store.pat_map_back.get(*param).copied(); - let child_binding_id = - self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); - let child_pat_id = - self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None }); - self.add_definition_to_binding(child_binding_id, child_pat_id); - if let Some(pat_syntax) = pat_syntax { - self.store.pat_map_back.insert(child_pat_id, pat_syntax); - } - let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); - if !hygiene.is_root() { - self.store.ident_hygiene.insert(expr.into(), hygiene); - } - statements.push(Statement::Let { - pat: child_pat_id, - type_ref: None, - initializer: Some(expr), - else_branch: None, - }); + let pat_syntax = self.store.pat_map_back.get(param.formal).copied(); if !is_simple_parameter { + // It needs to be mutable so the inner patterns can borrow it mutably (`ref mut`). + let child_binding_id = + self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); + let child_pat_id = + self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None }); + self.add_definition_to_binding(child_binding_id, child_pat_id); + if let Some(pat_syntax) = pat_syntax { + self.store.pat_map_back.insert(child_pat_id, pat_syntax); + } let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); if !hygiene.is_root() { self.store.ident_hygiene.insert(expr.into(), hygiene); } statements.push(Statement::Let { - pat: *param, + pat: child_pat_id, type_ref: None, initializer: Some(expr), else_branch: None, }); + } + let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); + if !hygiene.is_root() { + self.store.ident_hygiene.insert(expr.into(), hygiene); + } + statements.push(Statement::Let { + pat: param.formal, + type_ref: None, + initializer: Some(expr), + else_branch: None, + }); - let parent_binding_id = - self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); - let parent_pat_id = - self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None }); - self.add_definition_to_binding(parent_binding_id, parent_pat_id); - if let Some(pat_syntax) = pat_syntax { - self.store.pat_map_back.insert(parent_pat_id, pat_syntax); - } - *param = parent_pat_id; + let parent_binding_id = self.alloc_binding(name, BindingAnnotation::Mutable, hygiene); + let parent_pat_id = + self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None }); + self.add_definition_to_binding(parent_binding_id, parent_pat_id); + if let Some(pat_syntax) = pat_syntax { + self.store.pat_map_back.insert(parent_pat_id, pat_syntax); } + *param = Param { formal: parent_pat_id, user_written: param.formal }; } let coroutine = self.desugared_coroutine_expr( @@ -1105,8 +1115,8 @@ impl<'db> ExprCollector<'db> { fn collect( &mut self, - self_params: &mut ArrayVec<BindingId, 2>, - params: &mut [PatId], + self_param: &mut Option<Param<BindingId>>, + params: &mut [Param<PatId>], expr: Option<ast::Expr>, awaitable: Awaitable, is_async_fn: bool, @@ -1123,7 +1133,7 @@ impl<'db> ExprCollector<'db> { (false, false) => unreachable!(), }; this.lower_coroutine_body_with_moved_arguments( - self_params, + self_param, params, body, kind, @@ -1603,13 +1613,15 @@ impl<'db> ExprCollector<'db> { let closure_kind = if let Some(kind) = kind { // It's important that this expr is allocated immediately before the closure. // We rely on it for `coroutine_for_closure()`. + let mut args_with_user_written = args.iter().copied().map(Param::new).collect::<Vec<_>>(); body = this.lower_coroutine_body_with_moved_arguments( - &mut ArrayVec::new(), - &mut args, + &mut None, + &mut args_with_user_written, body, kind, CoroutineSource::Closure, ); + args.iter_mut().set_from(args_with_user_written.iter().map(|arg| arg.formal)); is_coroutine_closure = true; ClosureKind::CoroutineClosure(kind) diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 25e5711d1e..24ad23dbac 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -92,12 +92,12 @@ pub fn print_body_hir( }; if let DefWithBodyId::FunctionId(_) = owner { p.buf.push('('); - if let Some(self_param) = body.self_param() { - p.print_binding(self_param); + if let Some(self_param) = body.self_param { + p.print_binding(self_param.formal); p.buf.push_str(", "); } body.params.iter().for_each(|param| { - p.print_pat(*param); + p.print_pat(param.formal); p.buf.push_str(", "); }); // remove the last ", " in param list diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs index 7a2c8dc3ff..40f67fd266 100644 --- a/crates/hir-def/src/expr_store/scope.rs +++ b/crates/hir-def/src/expr_store/scope.rs @@ -5,7 +5,7 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use crate::{ BlockId, DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId, db::DefDatabase, - expr_store::{Body, ExpressionStore, HygieneId}, + expr_store::{Body, ExpressionStore, HygieneId, body::Param}, hir::{ Array, Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, generics::GenericParams, @@ -147,10 +147,10 @@ impl ExprScopes { ), }; let mut root = scopes.root_scope(); - if let Some(self_param) = body.self_param() { + if let Some(Param { formal: self_param, user_written: _ }) = body.self_param { scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param)); } - scopes.add_params_bindings(body, root, &body.params); + body.params.iter().for_each(|param| scopes.add_pat_bindings(body, root, param.formal)); compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut { root }, &mut root); scopes } @@ -248,10 +248,6 @@ impl ExprScopes { pattern.walk_child_pats(|pat| self.add_pat_bindings(store, scope, pat)); } - fn add_params_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, params: &[PatId]) { - params.iter().for_each(|pat| self.add_pat_bindings(store, scope, *pat)); - } - fn set_scope(&mut self, node: ExprId, scope: ScopeId) { self.scope_by_expr.insert(node, scope); } @@ -356,7 +352,7 @@ fn compute_expr_scopes( } Expr::Closure { args, body: body_expr, .. } => { let mut scope = scopes.new_scope(*scope); - scopes.add_params_bindings(store, scope, args); + args.iter().for_each(|arg| scopes.add_pat_bindings(store, scope, *arg)); compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); } Expr::Match { expr, arms } => { diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs index dda5a8189c..21bb6698af 100644 --- a/crates/hir-def/src/expr_store/tests/body.rs +++ b/crates/hir-def/src/expr_store/tests/body.rs @@ -652,9 +652,9 @@ fn async_fn_weird_param_patterns() { async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {} "#, expect![[r#" - fn main(self, param1, mut param2, mut <ra@gennew>0, mut param4, mut <ra@gennew>1) async { + fn main(self, mut param1, mut param2, mut <ra@gennew>0, mut param4, mut <ra@gennew>1) async { let self = self; - let mut param1 = param1; + let param1 = param1; let mut param2 = param2; let ref mut param2 = param2; let mut <ra@gennew>0 = <ra@gennew>0; diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index 3c69c6a44b..88a44d2e6a 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -63,8 +63,8 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> MissingUnsafe // Unsafety in function parameter patterns (that can only be union destructuring) // cannot be inserted into an unsafe block, so even with `unsafe_op_in_unsafe_fn` // it is turned off for unsafe functions. - for ¶m in &body.params { - visitor.walk_pat(param); + for param in &body.params { + visitor.walk_pat(param.formal); } } @@ -112,8 +112,8 @@ pub fn unsafe_operations_for_body( }; let mut visitor = UnsafeVisitor::new(db, infer, body, def.into(), &mut visitor_callback); visitor.walk_expr(body.root_expr()); - for ¶m in &body.params { - visitor.walk_pat(param); + for param in &body.params { + visitor.walk_pat(param.formal); } } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 5558d8fd47..1f6f6f551a 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -44,7 +44,7 @@ use hir_def::{ FunctionId, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, StaticId, TraitId, TupleFieldId, TupleId, VariantId, attrs::AttrFlags, - expr_store::{Body, ExpressionStore, HygieneId, path::Path}, + expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path}, hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::LangItems, layout::Integer, @@ -145,7 +145,9 @@ pub fn infer_query_with_inspect<'db>( } match def { - DefWithBodyId::FunctionId(f) => ctx.collect_fn(f, body.self_param(), &body.params), + DefWithBodyId::FunctionId(f) => { + ctx.collect_fn(f, body.self_param.map(|param| param.formal), &body.params) + } DefWithBodyId::ConstId(c) => ctx.collect_const(c, ConstSignature::of(db, c)), DefWithBodyId::StaticId(s) => ctx.collect_static(s, StaticSignature::of(db, s)), DefWithBodyId::VariantId(v) => { @@ -1699,7 +1701,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.return_ty = return_ty; } - fn collect_fn(&mut self, func: FunctionId, self_param: Option<BindingId>, params: &[PatId]) { + fn collect_fn( + &mut self, + func: FunctionId, + self_param: Option<BindingId>, + params: &[Param<PatId>], + ) { let data = FunctionSignature::of(self.db, func); let mut param_tys = self.with_ty_lowering( &data.store, @@ -1738,7 +1745,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let ty = param_tys.next().unwrap_or_else(|| self.table.next_ty_var(Span::Dummy)); let ty = self.process_user_written_ty(ty); - self.infer_top_pat(*pat, ty, PatOrigin::Param); + self.infer_top_pat(pat.formal, ty, PatOrigin::Param); } self.return_ty = match data.ret_type { Some(return_ty) => { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 4359a1acb5..d1277762eb 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -6,7 +6,7 @@ use base_db::Crate; use hir_def::{ AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, ItemContainerId, LocalFieldId, Lookup, TraitId, - expr_store::{Body, ExpressionStore, HygieneId, path::Path}, + expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ClosureKind, ExprId, ExprOrPatId, LabelId, Literal, MatchArm, Pat, PatId, RecordLitField, RecordSpread, @@ -2312,7 +2312,7 @@ pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: InferBodyId) -> Result let (store, root_expr, self_param, params) = match def { InferBodyId::DefWithBodyId(def) => { let body = Body::of(db, def); - (&**body, body.root_expr(), body.self_param(), &*body.params) + (&**body, body.root_expr(), body.self_param.map(|param| param.formal), &*body.params) } InferBodyId::AnonConstId(def) => { let loc = def.loc(db); @@ -2351,7 +2351,7 @@ pub fn lower_body_to_mir<'db>( infer: &InferenceResult, root_expr: ExprId, self_param: Option<BindingId>, - params: &[PatId], + params: &[Param<PatId>], ) -> 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() { @@ -2366,7 +2366,7 @@ pub fn lower_body_to_mir<'db>( store, infer, root_expr, - params.iter().copied().zip(param_tys), + params.iter().map(|param| param.formal).zip(param_tys), self_param, ) } else { diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index ce4eff701c..9ffe38e749 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -514,7 +514,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { for (def, krate) in defs { let (body, source_map) = Body::with_source_map(&db, def); let infer = InferenceResult::of(&db, def); - let self_param = body.self_param().map(|id| (id, source_map.self_param_syntax())); + let self_param = + body.self_param.map(|param| (param.formal, source_map.self_param_syntax())); infer_def(infer, body, source_map, self_param, krate); } diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs index bf0e60cf21..1fc556d04d 100644 --- a/crates/hir-ty/src/tests/closure_captures.rs +++ b/crates/hir-ty/src/tests/closure_captures.rs @@ -102,7 +102,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec // FIXME: Deduplicate this with hir::Local::sources(). let captured_local = capture.captured_local(); - let local_text_range = if body.self_params.contains(&captured_local) + let local_text_range = if body.is_any_self_param(captured_local) && let Some(source) = source_map.self_param_syntax() { format!("{:?}", text_range(db, source)) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index ed18482bf3..0095401ff5 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -217,7 +217,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re first = false; } - let pat_id = body.params[param.idx - body.self_param().is_some() as usize]; + let pat_id = body.params[param.idx - body.self_param.is_some() as usize].user_written; let pat_str = body.pretty_print_pat(db, owner, pat_id, true, f.edition()); f.write_str(&pat_str)?; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8f637a03e1..8d21b351e4 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2734,14 +2734,14 @@ impl<'db> Param<'db> { Callee::Def(CallableDefId::FunctionId(it)) => { let parent = DefWithBodyId::FunctionId(it); let body = Body::of(db, parent); - if let Some(self_param) = body.self_param().filter(|_| self.idx == 0) { + if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { Some(Local { parent: parent.into(), parent_infer: parent.into(), - binding_id: self_param, + binding_id: self_param.user_written, }) } else if let Pat::Bind { id, .. } = - &body[body.params[self.idx - body.self_param().is_some() as usize]] + &body[body.params[self.idx - body.self_param.is_some() as usize].user_written] { Some(Local { parent: parent.into(), @@ -4190,7 +4190,7 @@ impl Local { } ExpressionStoreOwnerId::Body(def_with_body_id) => { b = Body::with_source_map(db, def_with_body_id); - if b.0.self_params.contains(&self.binding_id) + if b.0.is_any_self_param(self.binding_id) && let Some(source) = b.1.self_param_syntax() { let root = source.file_syntax(db); @@ -4231,7 +4231,7 @@ impl Local { } ExpressionStoreOwnerId::Body(def_with_body_id) => { b = Body::with_source_map(db, def_with_body_id); - if b.0.self_params.contains(&self.binding_id) + if b.0.is_any_self_param(self.binding_id) && let Some(source) = b.1.self_param_syntax() { let root = source.file_syntax(db); diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 583b3e4bda..8118730f24 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -371,7 +371,7 @@ impl SourceToDefCtx<'_, '_> { .as_expression_store_owner()? .as_def_with_body()?; let body = Body::of(self.db, container); - Some((container, body.self_param()?)) + Some((container, body.self_param?.user_written)) } pub(super) fn label_to_def( &mut self, diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 2ac3bc8f74..fc03f82135 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -580,7 +580,7 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option<Type<'db>> { let binding = match self.body_or_sig.as_ref()? { BodyOrSig::Sig { .. } | BodyOrSig::VariantFields { .. } => return None, - BodyOrSig::Body { body, .. } => body.self_param()?, + BodyOrSig::Body { body, .. } => body.self_param?.formal, }; let ty = self.infer()?.binding_ty(binding); Some(self.ty(ty)) diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index a5203404b2..a14b30db42 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -1570,11 +1570,8 @@ async fn foo(arg: u32) -> u32 { } fn spawn<T>(_: T) {} fn main() { - spawn({ - let arg = 42; - async move { - bar(arg).await * 2 - } + spawn(async move { + bar(42).await * 2 }); } "#, @@ -1605,12 +1602,9 @@ async fn foo(arg: u32) -> u32 { } fn spawn<T>(_: T) {} fn main() { - spawn({ - let arg = 42; - async move { - bar(arg).await; - 42 - } + spawn(async move { + bar(42).await; + 42 }); } "#, @@ -1645,11 +1639,10 @@ fn spawn<T>(_: T) {} fn main() { let var = 42; spawn({ - let x = var; let y = var + 1; let z: &u32 = &var; async move { - bar(x).await; + bar(var).await; y + y + *z } }); diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 12ce457ea6..9fc89e90f2 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -2549,4 +2549,26 @@ fn main() { "#, ); } + + #[test] + fn async_fn_param() { + check( + r#" +async fn get_double_async(num$0: u32) -> u32 { + // ^^^ + num + // ^^^ read +} + "#, + ); + check( + r#" +async fn get_double_async((num$0,): (u32,)) -> u32 { + // ^^^ + num + // ^^^ read +} + "#, + ); + } } diff --git a/crates/ide/src/syntax_highlighting/test_data/async_fn_non_mut_param.html b/crates/ide/src/syntax_highlighting/test_data/async_fn_non_mut_param.html new file mode 100644 index 0000000000..46b2f67819 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/test_data/async_fn_non_mut_param.html @@ -0,0 +1,46 @@ + +<style> +body { margin: 0; } +pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } + +.lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } +.comment { color: #7F9F7F; } +.documentation { color: #629755; } +.intra_doc_link { font-style: italic; } +.injected { opacity: 0.65 ; } +.struct, .enum { color: #7CB8BB; } +.enum_variant { color: #BDE0F3; } +.string_literal { color: #CC9393; } +.field { color: #94BFF3; } +.function { color: #93E0E3; } +.parameter { color: #94BFF3; } +.text { color: #DCDCCC; } +.type { color: #7CB8BB; } +.builtin_type { color: #8CD0D3; } +.type_param { color: #DFAF8F; } +.attribute { color: #94BFF3; } +.numeric_literal { color: #BFEBBF; } +.bool_literal { color: #BFE6EB; } +.macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } +.derive { color: #94BFF3; font-style: italic; } +.module { color: #AFD8AF; } +.value_param { color: #DCDCCC; } +.variable { color: #DCDCCC; } +.format_specifier { color: #CC696B; } +.mutable { text-decoration: underline; } +.escape_sequence { color: #94BFF3; } +.keyword { color: #F0DFAF; font-weight: bold; } +.control { font-style: italic; } +.reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } +.unsafe { color: #BC8383; } +.deprecated { text-decoration: line-through; } + +.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } +.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } +</style> +<pre><code><span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function async declaration">get_double_async</span><span class="parenthesis">(</span><span class="value_param declaration">num</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="brace">{</span> + <span class="value_param">num</span> +<span class="brace">}</span></code></pre>
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index ab69578ed9..c0a923afbe 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -1584,3 +1584,16 @@ static STATIC: () = (); false, ); } + +#[test] +fn async_fn_non_mut_param() { + check_highlighting( + r#" +async fn get_double_async(num: u32) -> u32 { + num +} + "#, + expect_file!["./test_data/async_fn_non_mut_param.html"], + false, + ); +} |