Unnamed repository; edit this file 'description' to name the repository.
-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,
+ );
+}