@@ -656,6 +656,25 @@ trait UnusedDelimLint {
656
656
is_kw : bool ,
657
657
) ;
658
658
659
+ #[ inline]
660
+ fn impacts_followed_by_block ( e : & ast:: Expr ) -> bool {
661
+ return match e. kind {
662
+ ExprKind :: Block ( ..) | ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) => true ,
663
+ _ => Self :: is_followed_by_block ( e) ,
664
+ } ;
665
+ }
666
+
667
+ #[ inline]
668
+ fn is_followed_by_block ( e : & ast:: Expr ) -> bool {
669
+ return match e. kind {
670
+ ExprKind :: If ( ref cond, ..) if !matches ! ( cond. kind, ExprKind :: Let ( ..) ) => true ,
671
+ ExprKind :: While ( ref cond, ..) if !matches ! ( cond. kind, ExprKind :: Let ( ..) ) => true ,
672
+ ExprKind :: ForLoop { .. } => true ,
673
+ ExprKind :: Match ( _, _, ast:: MatchKind :: Prefix ) => true ,
674
+ _ => false ,
675
+ } ;
676
+ }
677
+
659
678
fn is_expr_delims_necessary (
660
679
inner : & ast:: Expr ,
661
680
ctx : UnusedDelimsCtx ,
@@ -1475,7 +1494,15 @@ declare_lint! {
1475
1494
"unnecessary braces around an expression"
1476
1495
}
1477
1496
1478
- declare_lint_pass ! ( UnusedBraces => [ UNUSED_BRACES ] ) ;
1497
+ #[ derive( Default ) ]
1498
+ pub ( crate ) struct UnusedBraces {
1499
+ // Used for tracking parent expressions that would immediately followed
1500
+ // by block. Storing false here indicates that expression itself is Block
1501
+ // expression. This is meant for to prevent report false positive cases.
1502
+ followed_by_block : Vec < bool > ,
1503
+ }
1504
+
1505
+ impl_lint_pass ! ( UnusedBraces => [ UNUSED_BRACES ] ) ;
1479
1506
1480
1507
impl UnusedDelimLint for UnusedBraces {
1481
1508
const DELIM_STR : & ' static str = "braces" ;
@@ -1505,6 +1532,11 @@ impl UnusedDelimLint for UnusedBraces {
1505
1532
// - the block does not have a label
1506
1533
// - the block is not `unsafe`
1507
1534
// - the block contains exactly one expression (do not lint `{ expr; }`)
1535
+ // - however, this does not lint if block is immediately followed by parent
1536
+ // expression's block, e.g. `if` and `match`, which may cause false positive.
1537
+ // ```
1538
+ // if return { return } {} else {}
1539
+ // ```
1508
1540
// - `followed_by_block` is true and the internal expr may contain a `{`
1509
1541
// - the block is not multiline (do not lint multiline match arms)
1510
1542
// ```
@@ -1524,9 +1556,18 @@ impl UnusedDelimLint for UnusedBraces {
1524
1556
// let _: A<{produces_literal!()}>;
1525
1557
// ```
1526
1558
// FIXME(const_generics): handle paths when #67075 is fixed.
1559
+ // if ctx == UnusedDelimsCtx::FunctionArg {
1560
+ // println!("{:?}", self.followed_by_block.last().copied().unwrap_or(false))
1561
+ // }
1527
1562
if let [ stmt] = inner. stmts . as_slice ( )
1528
1563
&& let ast:: StmtKind :: Expr ( ref expr) = stmt. kind
1529
1564
&& !Self :: is_expr_delims_necessary ( expr, ctx, followed_by_block)
1565
+ && ( ( ctx == UnusedDelimsCtx :: FunctionArg || ctx == UnusedDelimsCtx :: MethodArg )
1566
+ || !Self :: is_expr_delims_necessary (
1567
+ expr,
1568
+ ctx,
1569
+ self . followed_by_block . last ( ) . copied ( ) . unwrap_or ( false ) ,
1570
+ ) )
1530
1571
&& ( ctx != UnusedDelimsCtx :: AnonConst
1531
1572
|| ( matches ! ( expr. kind, ast:: ExprKind :: Lit ( _) )
1532
1573
&& !expr. span . from_expansion ( ) ) )
@@ -1564,6 +1605,10 @@ impl EarlyLintPass for UnusedBraces {
1564
1605
fn check_expr ( & mut self , cx : & EarlyContext < ' _ > , e : & ast:: Expr ) {
1565
1606
<Self as UnusedDelimLint >:: check_expr ( self , cx, e) ;
1566
1607
1608
+ if <Self as UnusedDelimLint >:: impacts_followed_by_block ( e) {
1609
+ self . followed_by_block . push ( <Self as UnusedDelimLint >:: is_followed_by_block ( e) ) ;
1610
+ }
1611
+
1567
1612
if let ExprKind :: Repeat ( _, ref anon_const) = e. kind {
1568
1613
self . check_unused_delims_expr (
1569
1614
cx,
@@ -1577,6 +1622,18 @@ impl EarlyLintPass for UnusedBraces {
1577
1622
}
1578
1623
}
1579
1624
1625
+ fn check_expr_post ( & mut self , _cx : & EarlyContext < ' _ > , e : & ast:: Expr ) {
1626
+ if <Self as UnusedDelimLint >:: impacts_followed_by_block ( e) {
1627
+ let followed_by_block =
1628
+ self . followed_by_block . pop ( ) . expect ( "check_expr and check_expr_post must balance" ) ;
1629
+ assert_eq ! (
1630
+ followed_by_block,
1631
+ Self :: is_followed_by_block( e) ,
1632
+ "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1633
+ ) ;
1634
+ }
1635
+ }
1636
+
1580
1637
fn check_generic_arg ( & mut self , cx : & EarlyContext < ' _ > , arg : & ast:: GenericArg ) {
1581
1638
if let ast:: GenericArg :: Const ( ct) = arg {
1582
1639
self . check_unused_delims_expr (
0 commit comments