about summary refs log tree commit diff
path: root/clippy_lints/src/loops/never_loop.rs
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints/src/loops/never_loop.rs')
-rw-r--r--clippy_lints/src/loops/never_loop.rs82
1 files changed, 51 insertions, 31 deletions
diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs
index 5f1fdf00be8..4f2092492aa 100644
--- a/clippy_lints/src/loops/never_loop.rs
+++ b/clippy_lints/src/loops/never_loop.rs
@@ -1,22 +1,23 @@
 use super::utils::make_iterator_snippet;
 use super::NEVER_LOOP;
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::consts::constant;
 use clippy_utils::higher::ForLoop;
 use clippy_utils::source::snippet;
+use clippy_utils::{consts::Constant, diagnostics::span_lint_and_then};
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
 use rustc_lint::LateContext;
 use rustc_span::Span;
 use std::iter::{once, Iterator};
 
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    block: &Block<'_>,
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    block: &Block<'tcx>,
     loop_id: HirId,
     span: Span,
     for_loop: Option<&ForLoop<'_>>,
 ) {
-    match never_loop_block(block, &mut Vec::new(), loop_id) {
+    match never_loop_block(cx, block, &mut Vec::new(), loop_id) {
         NeverLoopResult::AlwaysBreak => {
             span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
                 if let Some(ForLoop {
@@ -95,7 +96,12 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirI
     }
 }
 
-fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
+fn never_loop_block<'tcx>(
+    cx: &LateContext<'tcx>,
+    block: &Block<'tcx>,
+    ignore_ids: &mut Vec<HirId>,
+    main_loop_id: HirId,
+) -> NeverLoopResult {
     let iter = block
         .stmts
         .iter()
@@ -103,10 +109,10 @@ fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id
         .chain(block.expr.map(|expr| (expr, None)));
 
     iter.map(|(e, els)| {
-        let e = never_loop_expr(e, ignore_ids, main_loop_id);
+        let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
         // els is an else block in a let...else binding
         els.map_or(e, |els| {
-            combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id), ignore_ids)
+            combine_branches(e, never_loop_block(cx, els, ignore_ids, main_loop_id), ignore_ids)
         })
     })
     .fold(NeverLoopResult::Otherwise, combine_seq)
@@ -122,7 +128,12 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
 }
 
 #[allow(clippy::too_many_lines)]
-fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
+fn never_loop_expr<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'tcx>,
+    ignore_ids: &mut Vec<HirId>,
+    main_loop_id: HirId,
+) -> NeverLoopResult {
     match expr.kind {
         ExprKind::Unary(_, e)
         | ExprKind::Cast(e, _)
@@ -130,45 +141,51 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
         | ExprKind::Field(e, _)
         | ExprKind::AddrOf(_, _, e)
         | ExprKind::Repeat(e, _)
-        | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id),
-        ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id),
-        ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id),
+        | ExprKind::DropTemps(e) => never_loop_expr(cx, e, ignore_ids, main_loop_id),
+        ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, ignore_ids, main_loop_id),
+        ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, &mut es.iter(), ignore_ids, main_loop_id),
         ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all(
+            cx,
             &mut std::iter::once(receiver).chain(es.iter()),
             ignore_ids,
             main_loop_id,
         ),
         ExprKind::Struct(_, fields, base) => {
-            let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
+            let fields = never_loop_expr_all(cx, &mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
             if let Some(base) = base {
-                combine_seq(fields, never_loop_expr(base, ignore_ids, main_loop_id))
+                combine_seq(fields, never_loop_expr(cx, base, ignore_ids, main_loop_id))
             } else {
                 fields
             }
         },
-        ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
+        ExprKind::Call(e, es) => never_loop_expr_all(cx, &mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
         ExprKind::Binary(_, e1, e2)
         | ExprKind::Assign(e1, e2, _)
         | ExprKind::AssignOp(_, e1, e2)
-        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
+        | ExprKind::Index(e1, e2) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
         ExprKind::Loop(b, _, _, _) => {
             // Break can come from the inner loop so remove them.
-            absorb_break(never_loop_block(b, ignore_ids, main_loop_id))
+            absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id))
         },
         ExprKind::If(e, e2, e3) => {
-            let e1 = never_loop_expr(e, ignore_ids, main_loop_id);
-            let e2 = never_loop_expr(e2, ignore_ids, main_loop_id);
+            let e1 = never_loop_expr(cx, e, ignore_ids, main_loop_id);
+            let e2 = never_loop_expr(cx, e2, ignore_ids, main_loop_id);
+            // If we know the `if` condition evaluates to `true`, don't check everything past it; it
+            // should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable
+            if let Some(Constant::Bool(true)) = constant(cx, cx.typeck_results(), e) {
+                return combine_seq(e1, e2);
+            }
             let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
-                never_loop_expr(e, ignore_ids, main_loop_id)
+                never_loop_expr(cx, e, ignore_ids, main_loop_id)
             });
             combine_seq(e1, combine_branches(e2, e3, ignore_ids))
         },
         ExprKind::Match(e, arms, _) => {
-            let e = never_loop_expr(e, ignore_ids, main_loop_id);
+            let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
             if arms.is_empty() {
                 e
             } else {
-                let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
+                let arms = never_loop_expr_branch(cx, &mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
                 combine_seq(e, arms)
             }
         },
@@ -176,7 +193,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
             if l.is_some() {
                 ignore_ids.push(b.hir_id);
             }
-            let ret = never_loop_block(b, ignore_ids, main_loop_id);
+            let ret = never_loop_block(cx, b, ignore_ids, main_loop_id);
             if l.is_some() {
                 ignore_ids.pop();
             }
@@ -198,11 +215,11 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
         // checks if break targets a block instead of a loop
         ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
             .map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| {
-                never_loop_expr(e, ignore_ids, main_loop_id)
+                never_loop_expr(cx, e, ignore_ids, main_loop_id)
             }),
         ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
             combine_seq(
-                never_loop_expr(e, ignore_ids, main_loop_id),
+                never_loop_expr(cx, e, ignore_ids, main_loop_id),
                 NeverLoopResult::AlwaysBreak,
             )
         }),
@@ -211,12 +228,13 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
             .iter()
             .map(|(o, _)| match o {
                 InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
-                    never_loop_expr(expr, ignore_ids, main_loop_id)
+                    never_loop_expr(cx, expr, ignore_ids, main_loop_id)
                 },
                 InlineAsmOperand::Out { expr, .. } => {
-                    never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id)
+                    never_loop_expr_all(cx, &mut expr.iter().copied(), ignore_ids, main_loop_id)
                 },
                 InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
+                    cx,
                     &mut once(*in_expr).chain(out_expr.iter().copied()),
                     ignore_ids,
                     main_loop_id,
@@ -236,22 +254,24 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
     }
 }
 
-fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(
+fn never_loop_expr_all<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
+    cx: &LateContext<'tcx>,
     es: &mut T,
     ignore_ids: &mut Vec<HirId>,
     main_loop_id: HirId,
 ) -> NeverLoopResult {
-    es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
+    es.map(|e| never_loop_expr(cx, e, ignore_ids, main_loop_id))
         .fold(NeverLoopResult::Otherwise, combine_seq)
 }
 
-fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
+fn never_loop_expr_branch<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
+    cx: &LateContext<'tcx>,
     e: &mut T,
     ignore_ids: &mut Vec<HirId>,
     main_loop_id: HirId,
 ) -> NeverLoopResult {
     e.fold(NeverLoopResult::AlwaysBreak, |a, b| {
-        combine_branches(a, never_loop_expr(b, ignore_ids, main_loop_id), ignore_ids)
+        combine_branches(a, never_loop_expr(cx, b, ignore_ids, main_loop_id), ignore_ids)
     })
 }