From 6e85d5a47ec5b1695dc32ff189045c9433427a3b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 7 Oct 2025 09:46:40 +0800 Subject: [PATCH 1/2] Fix empty closure completion analysis Example --- ```rust fn foo() { bar(|| $0); } fn bar(f: impl FnOnce() -> u32) {} ``` **Before this PR**: ``` ty: impl FnOnce() -> u32, name: ? ``` **After this PR**: ``` ty: u32, name: ? ``` --- crates/ide-completion/src/context/analysis.rs | 9 ++++++++- crates/ide-completion/src/context/tests.rs | 13 ++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 77a94403abb9..890df18e9a29 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -756,7 +756,13 @@ fn expected_type_and_name<'db>( .map(|c| (Some(c.return_type()), None)) .unwrap_or((None, None)) }, - ast::ParamList(_) => (None, None), + ast::ParamList(it) => { + let closure = it.syntax().parent().and_then(ast::ClosureExpr::cast); + let ty = closure.and_then(|it| sema.type_of_expr(&it.into())); + ty.and_then(|ty| ty.original.as_callable(sema.db)) + .map(|c| (Some(c.return_type()), None)) + .unwrap_or((None, None)) + }, ast::Stmt(_) => (None, None), ast::Item(_) => (None, None), _ => { @@ -1938,6 +1944,7 @@ fn prev_special_biased_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { | T![|=] | T![&=] | T![^=] + | T![|] | T![return] | T![break] | T![continue] = prev.kind() diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index d9ec7915e3c7..5b0ce145c512 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -374,7 +374,6 @@ fn foo() -> u32 { #[test] fn expected_type_closure_param_return() { - // FIXME: make this work with `|| $0` check_expected_type_and_name( r#" //- minicore: fn @@ -382,6 +381,18 @@ fn foo() { bar(|| a$0); } +fn bar(f: impl FnOnce() -> u32) {} +"#, + expect![[r#"ty: u32, name: ?"#]], + ); + + check_expected_type_and_name( + r#" +//- minicore: fn +fn foo() { + bar(|| $0); +} + fn bar(f: impl FnOnce() -> u32) {} "#, expect![[r#"ty: u32, name: ?"#]], From 4d08b922b74186887086d4692bcaf67821cf5d41 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 10 Oct 2025 12:25:24 +0800 Subject: [PATCH 2/2] Not suggest type in closure parameter --- crates/ide-completion/src/context/analysis.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 890df18e9a29..6925816c719d 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -563,10 +563,10 @@ fn analyze<'db>( /// Calculate the expected type and name of the cursor position. fn expected_type_and_name<'db>( sema: &Semantics<'db, RootDatabase>, - token: &SyntaxToken, + self_token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option>, Option) { - let token = prev_special_biased_token_at_trivia(token.clone()); + let token = prev_special_biased_token_at_trivia(self_token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -758,7 +758,9 @@ fn expected_type_and_name<'db>( }, ast::ParamList(it) => { let closure = it.syntax().parent().and_then(ast::ClosureExpr::cast); - let ty = closure.and_then(|it| sema.type_of_expr(&it.into())); + let ty = closure + .filter(|_| it.syntax().text_range().end() <= self_token.text_range().start()) + .and_then(|it| sema.type_of_expr(&it.into())); ty.and_then(|ty| ty.original.as_callable(sema.db)) .map(|c| (Some(c.return_type()), None)) .unwrap_or((None, None))