@@ -563,6 +563,46 @@ declare_clippy_lint! {
563
563
"using `.chars().next()` to check if a string starts with a char"
564
564
}
565
565
566
+ declare_clippy_lint ! {
567
+ /// **What it does:** Checks for calls to `.or_insert_with` on `Entry`
568
+ /// (for `BTreeMap` or `HashMap`) with `Default::default` or equivalent
569
+ /// as the argument, and suggests `.or_default`.
570
+ ///
571
+ /// **Why is this bad?** There is a specific method for this, and using it can make
572
+ /// the code cleaner.
573
+ ///
574
+ /// **Known problems:** None.
575
+ ///
576
+ /// **Example:**
577
+ ///
578
+ /// ```rust
579
+ /// use std::collections::{BTreeMap, HashMap};
580
+ ///
581
+ /// fn btree(map: &mut BTreeMap<i32, i32>) -> i32 {
582
+ /// map.entry(42).or_insert_with(Default::default)
583
+ /// }
584
+ ///
585
+ /// fn hash(map: &mut HashMap<i32, i32>) -> i32 {
586
+ /// map.entry(42).or_insert_with(i32::default)
587
+ /// }
588
+ /// ```
589
+ /// both can be instead written as
590
+ /// ```rust
591
+ /// use std::collections::{BTreeMap, HashMap};
592
+ ///
593
+ /// fn btree(map: &mut BTreeMap<i32, i32>) -> i32 {
594
+ /// map.entry(42).or_default()
595
+ /// }
596
+ ///
597
+ /// fn hash(map: &mut HashMap<i32, i32>) -> i32 {
598
+ /// map.entry(42).or_default()
599
+ /// }
600
+ /// ```
601
+ pub ENTRY_OR_INSERT_WITH_DEFAULT ,
602
+ style,
603
+ "calling `or_insert_with` on an `Entry` with `Default::default`"
604
+ }
605
+
566
606
declare_clippy_lint ! {
567
607
/// **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
568
608
/// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
@@ -1394,6 +1434,7 @@ declare_lint_pass!(Methods => [
1394
1434
RESULT_MAP_OR_INTO_OPTION ,
1395
1435
OPTION_MAP_OR_NONE ,
1396
1436
BIND_INSTEAD_OF_MAP ,
1437
+ ENTRY_OR_INSERT_WITH_DEFAULT ,
1397
1438
OR_FUN_CALL ,
1398
1439
EXPECT_FUN_CALL ,
1399
1440
CHARS_NEXT_CMP ,
@@ -1515,6 +1556,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
1515
1556
[ "unwrap_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "unwrap_or" ) ,
1516
1557
[ "get_or_insert_with" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "get_or_insert" ) ,
1517
1558
[ "ok_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "ok_or" ) ,
1559
+ [ "or_insert_with" , ..] => {
1560
+ lint_entry_or_insert_with_default ( cx, method_spans[ 0 ] . with_hi ( expr. span . hi ( ) ) , arg_lists[ 0 ] )
1561
+ } ,
1518
1562
_ => { } ,
1519
1563
}
1520
1564
@@ -1709,6 +1753,28 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
1709
1753
}
1710
1754
}
1711
1755
1756
+ /// Checks for the `ENTRY_OR_INSERT_WITH_DEFAULT` lint.
1757
+ fn lint_entry_or_insert_with_default ( cx : & LateContext < ' tcx > , span : Span , args : & ' tcx [ hir:: Expr < ' _ > ] ) {
1758
+ if_chain ! {
1759
+ let ty = cx. typeck_results( ) . expr_ty( & args[ 0 ] ) ;
1760
+ if match_type( cx, ty, & paths:: BTREEMAP_ENTRY ) || match_type( cx, ty, & paths:: HASHMAP_ENTRY ) ;
1761
+ if let hir:: ExprKind :: Path ( arg_path) = & args[ 1 ] . kind;
1762
+ if let hir:: def:: Res :: Def ( _, arg_def_id) = cx. qpath_res( & arg_path, args[ 1 ] . hir_id) ;
1763
+ if match_def_path( cx, arg_def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
1764
+ then {
1765
+ span_lint_and_sugg(
1766
+ cx,
1767
+ ENTRY_OR_INSERT_WITH_DEFAULT ,
1768
+ span,
1769
+ "use of `or_insert_with` with `Default::default`" ,
1770
+ "replace with" ,
1771
+ "or_default()" . to_owned( ) ,
1772
+ Applicability :: MachineApplicable ,
1773
+ ) ;
1774
+ }
1775
+ }
1776
+ }
1777
+
1712
1778
/// Checks for the `OR_FUN_CALL` lint.
1713
1779
#[ allow( clippy:: too_many_lines) ]
1714
1780
fn lint_or_fun_call < ' tcx > (
0 commit comments