@@ -3,6 +3,7 @@ mod explicit_counter_loop;
3
3
mod explicit_into_iter_loop;
4
4
mod explicit_iter_loop;
5
5
mod for_kv_map;
6
+ mod infinite_loops;
6
7
mod iter_next_loop;
7
8
mod manual_find;
8
9
mod manual_flatten;
@@ -22,7 +23,7 @@ mod while_let_on_iterator;
22
23
23
24
use clippy_config:: msrvs:: Msrv ;
24
25
use clippy_utils:: higher;
25
- use rustc_hir:: { Expr , ExprKind , LoopSource , Pat } ;
26
+ use rustc_hir:: { self as hir , Expr , ExprKind , LoopSource , Pat } ;
26
27
use rustc_lint:: { LateContext , LateLintPass } ;
27
28
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
28
29
use rustc_span:: source_map:: Span ;
@@ -635,15 +636,59 @@ declare_clippy_lint! {
635
636
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
636
637
}
637
638
639
+ declare_clippy_lint ! {
640
+ /// ### What it does
641
+ /// Checks for infinite loops in a function where the return type is not `!`
642
+ /// and lint accordingly.
643
+ ///
644
+ /// ### Why is this bad?
645
+ /// A loop should be gently exited somewhere, or at lease mark its parent function as
646
+ /// never return (`!`).
647
+ ///
648
+ /// ### Example
649
+ /// ```no_run
650
+ /// fn run_forever() {
651
+ /// loop {
652
+ /// // do something
653
+ /// }
654
+ /// }
655
+ /// ```
656
+ /// If infinite loops are as intended:
657
+ /// ```no_run
658
+ /// fn run_forever() -> ! {
659
+ /// loop {
660
+ /// // do something
661
+ /// }
662
+ /// }
663
+ /// ```
664
+ /// Otherwise add a `break` or `return` condition:
665
+ /// ```no_run
666
+ /// fn run_forever() {
667
+ /// loop {
668
+ /// // do something
669
+ /// if condition {
670
+ /// break;
671
+ /// }
672
+ /// }
673
+ /// }
674
+ /// ```
675
+ #[ clippy:: version = "1.75.0" ]
676
+ pub INFINITE_LOOPS ,
677
+ restriction,
678
+ "possibly unintended infinite loops"
679
+ }
680
+
638
681
pub struct Loops {
639
682
msrv : Msrv ,
640
683
enforce_iter_loop_reborrow : bool ,
684
+ in_never_return_fn : bool ,
641
685
}
642
686
impl Loops {
643
687
pub fn new ( msrv : Msrv , enforce_iter_loop_reborrow : bool ) -> Self {
644
688
Self {
645
689
msrv,
646
690
enforce_iter_loop_reborrow,
691
+ in_never_return_fn : false ,
647
692
}
648
693
}
649
694
}
@@ -669,6 +714,7 @@ impl_lint_pass!(Loops => [
669
714
MANUAL_FIND ,
670
715
MANUAL_WHILE_LET_SOME ,
671
716
UNUSED_ENUMERATE_INDEX ,
717
+ INFINITE_LOOPS ,
672
718
] ) ;
673
719
674
720
impl < ' tcx > LateLintPass < ' tcx > for Loops {
@@ -707,10 +753,13 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
707
753
// check for `loop { if let {} else break }` that could be `while let`
708
754
// (also matches an explicit "match" instead of "if let")
709
755
// (even if the "match" or "if let" is used for declaration)
710
- if let ExprKind :: Loop ( block, _ , LoopSource :: Loop , _) = expr. kind {
756
+ if let ExprKind :: Loop ( block, label , LoopSource :: Loop , _) = expr. kind {
711
757
// also check for empty `loop {}` statements, skipping those in #[panic_handler]
712
758
empty_loop:: check ( cx, expr, block) ;
713
759
while_let_loop:: check ( cx, expr, block) ;
760
+ if !self . in_never_return_fn {
761
+ infinite_loops:: check ( cx, expr, block, label) ;
762
+ }
714
763
}
715
764
716
765
while_let_on_iterator:: check ( cx, expr) ;
@@ -722,6 +771,24 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
722
771
}
723
772
}
724
773
774
+ fn check_fn (
775
+ & mut self ,
776
+ _: & LateContext < ' tcx > ,
777
+ _: hir:: intravisit:: FnKind < ' tcx > ,
778
+ decl : & ' tcx hir:: FnDecl < ' tcx > ,
779
+ _: & ' tcx hir:: Body < ' tcx > ,
780
+ _: Span ,
781
+ _: rustc_span:: def_id:: LocalDefId ,
782
+ ) {
783
+ self . in_never_return_fn = matches ! (
784
+ decl. output,
785
+ hir:: FnRetTy :: Return ( hir:: Ty {
786
+ kind: hir:: TyKind :: Never ,
787
+ ..
788
+ } )
789
+ ) ;
790
+ }
791
+
725
792
extract_msrv_attr ! ( LateContext ) ;
726
793
}
727
794
0 commit comments