@@ -526,16 +526,20 @@ fn can_omit_optional_parentheses(expr: &Expr, context: &PyFormatContext) -> bool
526
526
&& has_parentheses ( expr, context) . is_some_and ( OwnParentheses :: is_non_empty)
527
527
}
528
528
529
- // Only use the layout if the first or last expression has parentheses of some sort, and
529
+ // Only use the layout if the first expression starts with parentheses
530
+ // or the last expression ends with parentheses of some sort, and
530
531
// those parentheses are non-empty.
531
- let first_parenthesized = visitor
532
- . first
533
- . is_some_and ( |first| is_parenthesized ( first, context) ) ;
534
- let last_parenthesized = visitor
532
+ if visitor
535
533
. last
536
- . is_some_and ( |last| is_parenthesized ( last, context) ) ;
537
-
538
- first_parenthesized || last_parenthesized
534
+ . is_some_and ( |last| is_parenthesized ( last, context) )
535
+ {
536
+ true
537
+ } else {
538
+ visitor
539
+ . first
540
+ . expression ( )
541
+ . is_some_and ( |first| is_parenthesized ( first, context) )
542
+ }
539
543
}
540
544
}
541
545
@@ -545,7 +549,7 @@ struct CanOmitOptionalParenthesesVisitor<'input> {
545
549
max_precedence_count : u32 ,
546
550
any_parenthesized_expressions : bool ,
547
551
last : Option < & ' input Expr > ,
548
- first : Option < & ' input Expr > ,
552
+ first : First < ' input > ,
549
553
context : & ' input PyFormatContext < ' input > ,
550
554
}
551
555
@@ -557,7 +561,7 @@ impl<'input> CanOmitOptionalParenthesesVisitor<'input> {
557
561
max_precedence_count : 0 ,
558
562
any_parenthesized_expressions : false ,
559
563
last : None ,
560
- first : None ,
564
+ first : First :: None ,
561
565
}
562
566
}
563
567
@@ -670,6 +674,7 @@ impl<'input> CanOmitOptionalParenthesesVisitor<'input> {
670
674
if op. is_invert ( ) {
671
675
self . update_max_precedence ( OperatorPrecedence :: BitwiseInversion ) ;
672
676
}
677
+ self . first . set_if_none ( First :: Token ) ;
673
678
}
674
679
675
680
// `[a, b].test.test[300].dot`
@@ -706,17 +711,22 @@ impl<'input> CanOmitOptionalParenthesesVisitor<'input> {
706
711
self . update_max_precedence ( OperatorPrecedence :: String ) ;
707
712
}
708
713
709
- Expr :: Tuple ( _)
710
- | Expr :: NamedExpr ( _)
711
- | Expr :: GeneratorExp ( _)
712
- | Expr :: Lambda ( _)
714
+ // Expressions with sub expressions but a preceding token
715
+ // Mark this expression as first expression and not the sub expression.
716
+ Expr :: Lambda ( _)
713
717
| Expr :: Await ( _)
714
718
| Expr :: Yield ( _)
715
719
| Expr :: YieldFrom ( _)
720
+ | Expr :: Starred ( _) => {
721
+ self . first . set_if_none ( First :: Token ) ;
722
+ }
723
+
724
+ Expr :: Tuple ( _)
725
+ | Expr :: NamedExpr ( _)
726
+ | Expr :: GeneratorExp ( _)
716
727
| Expr :: FormattedValue ( _)
717
728
| Expr :: FString ( _)
718
729
| Expr :: Constant ( _)
719
- | Expr :: Starred ( _)
720
730
| Expr :: Name ( _)
721
731
| Expr :: Slice ( _)
722
732
| Expr :: IpyEscapeCommand ( _) => { }
@@ -741,8 +751,32 @@ impl<'input> PreorderVisitor<'input> for CanOmitOptionalParenthesesVisitor<'inpu
741
751
self . visit_subexpression ( expr) ;
742
752
}
743
753
744
- if self . first . is_none ( ) {
745
- self . first = Some ( expr) ;
754
+ self . first . set_if_none ( First :: Expression ( expr) ) ;
755
+ }
756
+ }
757
+
758
+ #[ derive( Copy , Clone , Debug ) ]
759
+ enum First < ' a > {
760
+ None ,
761
+
762
+ /// Expression starts with a non-parentheses token. E.g. `not a`
763
+ Token ,
764
+
765
+ Expression ( & ' a Expr ) ,
766
+ }
767
+
768
+ impl < ' a > First < ' a > {
769
+ #[ inline]
770
+ fn set_if_none ( & mut self , first : First < ' a > ) {
771
+ if matches ! ( self , First :: None ) {
772
+ * self = first;
773
+ }
774
+ }
775
+
776
+ fn expression ( self ) -> Option < & ' a Expr > {
777
+ match self {
778
+ First :: None | First :: Token => None ,
779
+ First :: Expression ( expr) => Some ( expr) ,
746
780
}
747
781
}
748
782
}
0 commit comments