@@ -6,7 +6,7 @@ use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
66use ide_db:: { SnippetCap , SymbolKind } ;
77use itertools:: Itertools ;
88use stdx:: { format_to, to_lower_snake_case} ;
9- use syntax:: { ast, format_smolstr, AstNode , Edition , SmolStr , SyntaxKind , ToSmolStr , T } ;
9+ use syntax:: { ast, format_smolstr, match_ast , AstNode , Edition , SmolStr , SyntaxKind , ToSmolStr , T } ;
1010
1111use crate :: {
1212 context:: { CompletionContext , DotAccess , DotAccessKind , PathCompletionCtx , PathKind } ,
@@ -278,31 +278,44 @@ pub(super) fn add_call_parens<'b>(
278278 ( snippet, "(…)" )
279279 } ;
280280 if ret_type. is_unit ( ) && ctx. config . add_semicolon_to_unit {
281- let next_non_trivia_token =
282- std:: iter:: successors ( ctx. token . next_token ( ) , |it| it. next_token ( ) )
283- . find ( |it| !it. kind ( ) . is_trivia ( ) ) ;
284- let in_match_arm = ctx. token . parent_ancestors ( ) . try_for_each ( |ancestor| {
285- if ast:: MatchArm :: can_cast ( ancestor. kind ( ) ) {
286- ControlFlow :: Break ( true )
287- } else if matches ! ( ancestor. kind( ) , SyntaxKind :: EXPR_STMT | SyntaxKind :: BLOCK_EXPR ) {
288- ControlFlow :: Break ( false )
289- } else {
290- ControlFlow :: Continue ( ( ) )
281+ let inside_closure_ret = ctx. token . parent_ancestors ( ) . try_for_each ( |ancestor| {
282+ match_ast ! {
283+ match ancestor {
284+ ast:: BlockExpr ( _) => ControlFlow :: Break ( false ) ,
285+ ast:: ClosureExpr ( _) => ControlFlow :: Break ( true ) ,
286+ _ => ControlFlow :: Continue ( ( ) )
287+ }
291288 }
292289 } ) ;
293- // FIXME: This will assume expr macros are not inside match, we need to somehow go to the "parent" of the root node.
294- let in_match_arm = match in_match_arm {
295- ControlFlow :: Continue ( ( ) ) => false ,
296- ControlFlow :: Break ( it) => it,
297- } ;
298- let complete_token = if in_match_arm { T ! [ , ] } else { T ! [ ; ] } ;
299- if next_non_trivia_token. map ( |it| it. kind ( ) ) != Some ( complete_token) {
300- cov_mark:: hit!( complete_semicolon) ;
301- let ch = if in_match_arm { ',' } else { ';' } ;
302- if snippet. ends_with ( "$0" ) {
303- snippet. insert ( snippet. len ( ) - "$0" . len ( ) , ch) ;
304- } else {
305- snippet. push ( ch) ;
290+
291+ if inside_closure_ret != ControlFlow :: Break ( true ) {
292+ let next_non_trivia_token =
293+ std:: iter:: successors ( ctx. token . next_token ( ) , |it| it. next_token ( ) )
294+ . find ( |it| !it. kind ( ) . is_trivia ( ) ) ;
295+ let in_match_arm = ctx. token . parent_ancestors ( ) . try_for_each ( |ancestor| {
296+ if ast:: MatchArm :: can_cast ( ancestor. kind ( ) ) {
297+ ControlFlow :: Break ( true )
298+ } else if matches ! ( ancestor. kind( ) , SyntaxKind :: EXPR_STMT | SyntaxKind :: BLOCK_EXPR )
299+ {
300+ ControlFlow :: Break ( false )
301+ } else {
302+ ControlFlow :: Continue ( ( ) )
303+ }
304+ } ) ;
305+ // FIXME: This will assume expr macros are not inside match, we need to somehow go to the "parent" of the root node.
306+ let in_match_arm = match in_match_arm {
307+ ControlFlow :: Continue ( ( ) ) => false ,
308+ ControlFlow :: Break ( it) => it,
309+ } ;
310+ let complete_token = if in_match_arm { T ! [ , ] } else { T ! [ ; ] } ;
311+ if next_non_trivia_token. map ( |it| it. kind ( ) ) != Some ( complete_token) {
312+ cov_mark:: hit!( complete_semicolon) ;
313+ let ch = if in_match_arm { ',' } else { ';' } ;
314+ if snippet. ends_with ( "$0" ) {
315+ snippet. insert ( snippet. len ( ) - "$0" . len ( ) , ch) ;
316+ } else {
317+ snippet. push ( ch) ;
318+ }
306319 }
307320 }
308321 }
@@ -886,6 +899,27 @@ fn bar() {
886899 v => foo()$0,
887900 }
888901}
902+ "# ,
903+ ) ;
904+ }
905+
906+ #[ test]
907+ fn no_semicolon_in_closure_ret ( ) {
908+ check_edit (
909+ r#"foo"# ,
910+ r#"
911+ fn foo() {}
912+ fn baz(_: impl FnOnce()) {}
913+ fn bar() {
914+ baz(|| fo$0);
915+ }
916+ "# ,
917+ r#"
918+ fn foo() {}
919+ fn baz(_: impl FnOnce()) {}
920+ fn bar() {
921+ baz(|| foo()$0);
922+ }
889923"# ,
890924 ) ;
891925 }
0 commit comments