@@ -264,53 +264,115 @@ pub fn arguments_grouped_layout(
264264 args : & [ Argument ] ,
265265 f : & Formatter < ' _ , ' _ > ,
266266) -> Option < GroupedCallArgumentLayout > {
267- if should_group_first_argument ( call_like_span, args, f) {
268- Some ( GroupedCallArgumentLayout :: GroupedFirstArgument )
269- } else if should_group_last_argument ( call_like_span, args, f) {
270- Some ( GroupedCallArgumentLayout :: GroupedLastArgument )
267+ // For exactly 2 arguments, we need to check both grouping strategies.
268+ // To avoid redundant `can_group_expression_argument` calls, we handle this case specially.
269+ if args. len ( ) == 2 {
270+ let [ first, second] = args else { unreachable ! ( "args.len() == 2 guarantees two elements" ) } ;
271+ let first = first. as_expression ( ) ?;
272+ let second = second. as_expression ( ) ?;
273+
274+ // Call `can_group_expression_argument` only once for the second argument
275+ let second_can_group = can_group_expression_argument ( second, f) ;
276+ let second_can_group_fn = || second_can_group;
277+
278+ // Check if we should group the last argument (second)
279+ if should_group_last_argument_impl (
280+ call_like_span,
281+ Some ( first) ,
282+ second,
283+ second_can_group_fn,
284+ f,
285+ ) {
286+ return Some ( GroupedCallArgumentLayout :: GroupedLastArgument ) ;
287+ }
288+
289+ // Check if we should group the first argument instead
290+ // Reuse the already-computed `second_can_group` value
291+ should_group_first_argument_impl ( call_like_span, first, second, second_can_group, f)
292+ . then_some ( GroupedCallArgumentLayout :: GroupedFirstArgument )
271293 } else {
272- None
294+ // For other cases (not exactly 2 arguments), only check last argument grouping
295+ should_group_last_argument ( call_like_span, args, f)
296+ . then_some ( GroupedCallArgumentLayout :: GroupedLastArgument )
273297 }
274298}
275299
276300/// Checks if the first argument requires grouping
277- fn should_group_first_argument (
301+ fn should_group_first_argument_impl (
278302 call_like_span : Span ,
279- args : & [ Argument ] ,
303+ first : & Expression ,
304+ second : & Expression ,
305+ second_can_group : bool ,
280306 f : & Formatter < ' _ , ' _ > ,
281307) -> bool {
282- let mut iter = args. iter ( ) ;
283- match ( iter. next ( ) . and_then ( |a| a. as_expression ( ) ) , iter. next ( ) . and_then ( |a| a. as_expression ( ) ) )
284- {
285- ( Some ( first) , Some ( second) ) if iter. next ( ) . is_none ( ) => {
286- match & first {
287- Expression :: FunctionExpression ( _) => { }
288- // Arrow expressions that are a plain expression or are a chain
289- // don't get grouped as the first argument, since they'll either
290- // fit entirely on the line or break fully. Only a single arrow
291- // with a block body can be grouped to collapse the braces.
292- Expression :: ArrowFunctionExpression ( arrow) => {
293- if arrow. expression {
294- return false ;
295- }
296- }
297- _ => return false ,
308+ match first {
309+ Expression :: FunctionExpression ( _) => { }
310+ // Arrow expressions that are a plain expression or are a chain
311+ // don't get grouped as the first argument, since they'll either
312+ // fit entirely on the line or break fully. Only a single arrow
313+ // with a block body can be grouped to collapse the braces.
314+ Expression :: ArrowFunctionExpression ( arrow) => {
315+ if arrow. expression {
316+ return false ;
298317 }
318+ }
319+ _ => return false ,
320+ }
321+
322+ if matches ! (
323+ second,
324+ Expression :: ArrowFunctionExpression ( _)
325+ | Expression :: FunctionExpression ( _)
326+ | Expression :: ConditionalExpression ( _)
327+ ) {
328+ return false ;
329+ }
299330
300- if matches ! (
301- second,
302- Expression :: ArrowFunctionExpression ( _)
303- | Expression :: FunctionExpression ( _)
304- | Expression :: ConditionalExpression ( _)
305- ) {
331+ !f. comments ( ) . has_comment ( call_like_span. start , first. span ( ) , second. span ( ) . start )
332+ && !second_can_group
333+ && is_relatively_short_argument ( second)
334+ }
335+
336+ /// Core logic for checking if the last argument should be grouped.
337+ /// Takes the penultimate argument as an Expression for the 2-argument case,
338+ /// or extracts it from the arguments array for other cases.
339+ fn should_group_last_argument_impl (
340+ call_like_span : Span ,
341+ penultimate : Option < & Expression > ,
342+ last : & Expression ,
343+ last_can_group_fn : impl FnOnce ( ) -> bool ,
344+ f : & Formatter < ' _ , ' _ > ,
345+ ) -> bool {
346+ // Check if penultimate and last are the same type (both Object or both Array)
347+ if let Some ( penultimate) = penultimate
348+ && matches ! (
349+ ( penultimate, last) ,
350+ ( Expression :: ObjectExpression ( _) , Expression :: ObjectExpression ( _) )
351+ | ( Expression :: ArrayExpression ( _) , Expression :: ArrayExpression ( _) )
352+ )
353+ {
354+ return false ;
355+ }
356+
357+ let previous_span = penultimate. map_or ( call_like_span. start , |e| e. span ( ) . end ) ;
358+ if f. comments ( ) . has_comment ( previous_span, last. span ( ) , call_like_span. end ) {
359+ return false ;
360+ }
361+
362+ if !last_can_group_fn ( ) {
363+ return false ;
364+ }
365+
366+ match last {
367+ Expression :: ArrayExpression ( array) if penultimate. is_some ( ) => {
368+ // Not for `useEffect`
369+ if matches ! ( penultimate, Some ( Expression :: ArrowFunctionExpression ( _) ) ) {
306370 return false ;
307371 }
308372
309- !f. comments ( ) . has_comment ( call_like_span. start , first. span ( ) , second. span ( ) . start )
310- && !can_group_expression_argument ( second, f)
311- && is_relatively_short_argument ( second)
373+ !can_concisely_print_array_list ( array. span , & array. elements , f)
312374 }
313- _ => false ,
375+ _ => true ,
314376 }
315377}
316378
@@ -321,50 +383,13 @@ fn should_group_last_argument(
321383 f : & Formatter < ' _ , ' _ > ,
322384) -> bool {
323385 let mut iter = args. iter ( ) ;
324- let last = iter. next_back ( ) ;
325-
326- match last. and_then ( |arg| arg. as_expression ( ) ) {
327- Some ( last) => {
328- let penultimate = iter. next_back ( ) ;
329- if let Some ( penultimate) = & penultimate
330- && matches ! (
331- ( penultimate, last) ,
332- ( Argument :: ObjectExpression ( _) , Expression :: ObjectExpression ( _) )
333- | ( Argument :: ArrayExpression ( _) , Expression :: ArrayExpression ( _) )
334- )
335- {
336- return false ;
337- }
338-
339- let previous_span = penultimate. map_or ( call_like_span. start , |a| a. span ( ) . end ) ;
340- if f. comments ( ) . has_comment ( previous_span, last. span ( ) , call_like_span. end ) {
341- return false ;
342- }
343-
344- if !can_group_expression_argument ( last, f) {
345- return false ;
346- }
347-
348- match last {
349- Expression :: ArrayExpression ( array) if args. len ( ) > 1 => {
350- // Not for `useEffect`
351- if args. len ( ) == 2
352- && matches ! ( penultimate, Some ( Argument :: ArrowFunctionExpression ( _) ) )
353- {
354- return false ;
355- }
356-
357- if can_concisely_print_array_list ( array. span , & array. elements , f) {
358- return false ;
359- }
386+ let Some ( last) = iter. next_back ( ) . unwrap ( ) . as_expression ( ) else {
387+ return false ;
388+ } ;
360389
361- true
362- }
363- _ => true ,
364- }
365- }
366- _ => false ,
367- }
390+ let penultimate = iter. next_back ( ) . and_then ( |arg| arg. as_expression ( ) ) ;
391+ let last_can_group_fn = || can_group_expression_argument ( last, f) ;
392+ should_group_last_argument_impl ( call_like_span, penultimate, last, last_can_group_fn, f)
368393}
369394
370395/// Check if `ty` is a relatively simple type annotation, allowing a few
0 commit comments