diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 91faafaf843e..82140772a82d 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -1072,6 +1072,7 @@ impl<'db> ExprCollector<'db> { let mut statements = Vec::with_capacity(usize::from(self_param.is_some()) + (params.len() * 2)); + let mut parameter_names = FxIndexSet::default(); if let Some(self_param) = self_param { let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param.formal]; @@ -1094,7 +1095,8 @@ impl<'db> ExprCollector<'db> { } for param in params { - let (name, hygiene, is_simple_parameter) = match self.store.pats[param.formal] { + let user_written_pat = param.formal; + let (mut name, mut 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 = __argN;` statement. In this case, we do not rename the parameter. Pat::Bind { id, subpat: None, .. } @@ -1113,6 +1115,23 @@ impl<'db> ExprCollector<'db> { } _ => (self.generate_new_name(), HygieneId::ROOT, false), }; + // `let b = b` normally resolves the initializer to the existing `b`. rustc's + // desugared path records which formal parameter it means, while ours is resolved by + // name. If an earlier parameter pattern introduced the same name, give this later + // formal a synthetic name so the move still resolves to it. + if parameter_names.contains(&(name.clone(), hygiene)) { + name = self.generate_new_name(); + hygiene = HygieneId::ROOT; + } + let mut parameter_pats = vec![user_written_pat]; + while let Some(pat_id) = parameter_pats.pop() { + let pat = &self.store.pats[pat_id]; + if let &Pat::Bind { id, .. } = pat { + let binding = &self.store.bindings[id]; + parameter_names.insert((binding.name.clone(), binding.hygiene)); + } + pat.walk_child_pats(|pat_id| parameter_pats.push(pat_id)); + } 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`). @@ -1153,7 +1172,7 @@ impl<'db> ExprCollector<'db> { 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 }; + *param = Param { formal: parent_pat_id, user_written: user_written_pat }; } let coroutine = self.desugared_coroutine_expr( diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs index aa3165670be3..52ce7c37cf8d 100644 --- a/crates/hir-def/src/expr_store/tests/body.rs +++ b/crates/hir-def/src/expr_store/tests/body.rs @@ -528,6 +528,95 @@ async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, ) } +#[test] +fn async_closure_duplicate_params() { + pretty_print( + r#" +fn test(temp: usize) { + async |b, b| { temp }; +} +"#, + expect![[r#" + fn test(temp) { + async |mut b, mut 0| { + let b = b; + let b = 0; + { + temp + } + }; + }"#]], + ); +} + +#[test] +fn async_closure_duplicate_nested_params() { + pretty_print( + r#" +fn test(temp: usize) { + async |(b, c), b| { temp }; +} +"#, + expect![[r#" + fn test(temp) { + async |mut 0, mut 1| { + let mut 0 = 0; + let (b, c) = 0; + let b = 1; + { + temp + } + }; + }"#]], + ); +} + +#[test] +fn async_closure_same_name_with_different_hygiene() { + pretty_print( + r#" +macro_rules! introduced { + () => { b }; +} + +fn test(temp: usize) { + async |introduced!(): usize, b: usize| { temp }; +} +"#, + expect![[r#" + fn test(temp) { + async |mut b: usize, mut b: usize| { + let b = b; + let b = b; + { + temp + } + }; + }"#]], + ); +} + +#[test] +fn async_closure_distinct_params_keep_names() { + pretty_print( + r#" +fn test(temp: usize) { + async |a, b| { temp }; +} +"#, + expect![[r#" + fn test(temp) { + async |mut a, mut b| { + let a = a; + let b = b; + { + temp + } + }; + }"#]], + ); +} + #[test] fn array_element_cfg() { pretty_print( diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index cdc0a8ab9e36..1d8ff1616557 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -3192,3 +3192,25 @@ fn main() { "#, ); } + +#[test] +fn regression_22821() { + check_no_mismatches( + r#" +fn test(temp: usize) { + async |b, b| { temp }; +} + "#, + ); +} + +#[test] +fn regression_22821_complex_param_pattern() { + check_no_mismatches( + r#" +fn test(temp: usize) { + async |(b, c), b| { temp }; +} + "#, + ); +}