Unnamed repository; edit this file 'description' to name the repository.
Fix handling of params of coroutine fns
They are copied into locals. In some places we want the new local, and in some cases we want the parameter. So store both. Also: - Make sure that the source map AST-to-PatId mapping contains the local and not the param, since this is what IDE needs. - Make sure the mutability shown in the IDE is correct, by using the user-written pattern for the local even when the param is a plain binding (in which case we previously didn't exchange the param's and the local's patterns). - Include param patterns in the root expr for the body, by lowering them inside `with_expr_root()`.
Chayim Refael Friedman 13 days ago
parent bd24a5d · commit 88887b0
-rw-r--r--crates/hir-def/src/expr_store/body.rs42
-rw-r--r--crates/hir-def/src/expr_store/lower.rs194
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs6
-rw-r--r--crates/hir-def/src/expr_store/scope.rs12
-rw-r--r--crates/hir-def/src/expr_store/tests/body.rs4
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs8
-rw-r--r--crates/hir-ty/src/infer.rs15
-rw-r--r--crates/hir-ty/src/mir/lower.rs8
-rw-r--r--crates/hir-ty/src/tests.rs3
-rw-r--r--crates/hir-ty/src/tests/closure_captures.rs2
-rw-r--r--crates/hir/src/display.rs2
-rw-r--r--crates/hir/src/lib.rs10
-rw-r--r--crates/hir/src/semantics/source_to_def.rs2
-rw-r--r--crates/hir/src/source_analyzer.rs2
-rw-r--r--crates/ide-assists/src/handlers/inline_call.rs19
-rw-r--r--crates/ide/src/highlight_related.rs22
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/async_fn_non_mut_param.html46
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs13
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 a69755baf6..7a89f9fa77 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(&param) {
- 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(&param) {
+ 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,
@@ -1599,13 +1609,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 98cbcb2450..732f4e3435 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 &param 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 &param 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 a09dcaafbb..045b85b7bc 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) => {
@@ -1695,7 +1697,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,
@@ -1734,7 +1741,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 f676a700cc..418e59d064 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2737,14 +2737,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(),
@@ -4193,7 +4193,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);
@@ -4234,7 +4234,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 687f130af4..7cb02fb027 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 8897e59355..1099cfdaec 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">-&gt;</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,
+ );
+}