about summary refs log tree commit diff
path: root/compiler/rustc_mir_build/src/thir
diff options
context:
space:
mode:
authorCaio <c410.f3r@gmail.com>2021-08-08 11:49:13 -0300
committerCaio <c410.f3r@gmail.com>2021-08-15 16:18:26 -0300
commit6aa9937a768bf13e5f7bd0ee6dd8579403b39058 (patch)
treead4c8d3c2233b953d6088456650d26972c3d581b /compiler/rustc_mir_build/src/thir
parent2d9f2eae84dc3988c5463fdd1b124a9b695447d7 (diff)
downloadrust-6aa9937a768bf13e5f7bd0ee6dd8579403b39058.tar.gz
rust-6aa9937a768bf13e5f7bd0ee6dd8579403b39058.zip
Introduce hir::ExprKind::Let - Take 2
Diffstat (limited to 'compiler/rustc_mir_build/src/thir')
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs192
-rw-r--r--compiler/rustc_mir_build/src/thir/visit.rs3
3 files changed, 135 insertions, 63 deletions
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index c3908ddd4fb..2b0aa41a4bd 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -590,6 +590,9 @@ impl<'tcx> Cx<'tcx> {
                 },
                 Err(err) => bug!("invalid loop id for continue: {}", err),
             },
+            hir::ExprKind::Let(ref pat, ref expr, _) => {
+                ExprKind::Let { expr: self.mirror_expr(expr), pat: self.pattern_from_hir(pat) }
+            }
             hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
                 cond: self.mirror_expr(cond),
                 then: self.mirror_expr(then),
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 57ea6c86e7a..50cbe0f71f5 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -55,9 +55,10 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
 
     fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
         intravisit::walk_expr(self, ex);
-
-        if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.kind {
-            self.check_match(scrut, arms, source);
+        match &ex.kind {
+            hir::ExprKind::Match(scrut, arms, source) => self.check_match(scrut, arms, *source),
+            hir::ExprKind::Let(pat, scrut, span) => self.check_let(pat, scrut, *span),
+            _ => {}
         }
     }
 
@@ -117,6 +118,31 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         check_for_bindings_named_same_as_variants(self, pat);
     }
 
+    fn let_source(&mut self, pat: &'tcx hir::Pat<'tcx>, _expr: &hir::Expr<'_>) -> LetSource {
+        let hir = self.tcx.hir();
+        let parent = hir.get_parent_node(pat.hir_id);
+        let parent_parent = hir.get_parent_node(parent);
+        let parent_parent_node = hir.get(parent_parent);
+
+        let parent_parent_parent = hir.get_parent_node(parent_parent);
+        let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
+        let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
+
+        if let hir::Node::Expr(hir::Expr {
+            kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
+            ..
+        }) = parent_parent_parent_parent_node
+        {
+            LetSource::WhileLet
+        } else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) =
+            parent_parent_node
+        {
+            LetSource::IfLet
+        } else {
+            LetSource::GenericLet
+        }
+    }
+
     fn lower_pattern<'p>(
         &self,
         cx: &mut MatchCheckCtxt<'p, 'tcx>,
@@ -144,6 +170,14 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         }
     }
 
+    fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
+        self.check_patterns(pat);
+        let ls = self.let_source(pat, expr);
+        let mut cx = self.new_cx(expr.hir_id);
+        let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
+        check_let_reachability(&mut cx, ls, pat.hir_id, &tpat, span);
+    }
+
     fn check_match(
         &mut self,
         scrut: &hir::Expr<'_>,
@@ -158,7 +192,13 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
             if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
                 self.check_patterns(pat);
                 let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
-                check_if_let_guard(&mut cx, &tpat, pat.hir_id);
+                check_let_reachability(
+                    &mut cx,
+                    LetSource::IfLetGuard,
+                    pat.hir_id,
+                    &tpat,
+                    tpat.span,
+                );
             }
         }
 
@@ -181,8 +221,16 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
         let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
 
-        // Report unreachable arms.
-        report_arm_reachability(&cx, &report, source);
+        report_arm_reachability(&cx, &report, |_, arm_span, arm_hir_id, catchall| {
+            match source {
+                hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
+                    unreachable_pattern(cx.tcx, arm_span, arm_hir_id, catchall);
+                }
+                // Unreachable patterns in try and await expressions occur when one of
+                // the arms are an uninhabited type. Which is OK.
+                hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
+            }
+        });
 
         // Check if the match is exhaustive.
         // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
@@ -349,89 +397,99 @@ fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<
     });
 }
 
-fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
-    tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
-        hir::MatchSource::IfLetDesugar { .. } => {
-            let mut diag = lint.build("irrefutable `if let` pattern");
-            diag.note("this pattern will always match, so the `if let` is useless");
-            diag.help("consider replacing the `if let` with a `let`");
+fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>) {
+    macro_rules! emit_diag {
+        (
+            $lint:expr,
+            $source_name:expr,
+            $note_sufix:expr,
+            $help_sufix:expr
+        ) => {{
+            let mut diag = $lint.build(concat!("irrefutable ", $source_name, " pattern"));
+            diag.note(concat!("this pattern will always match, so the ", $note_sufix));
+            diag.help(concat!("consider ", $help_sufix));
             diag.emit()
+        }};
+    }
+
+    tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match ls {
+        LetSource::GenericLet => {
+            emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
         }
-        hir::MatchSource::WhileLetDesugar => {
-            let mut diag = lint.build("irrefutable `while let` pattern");
-            diag.note("this pattern will always match, so the loop will never exit");
-            diag.help("consider instead using a `loop { ... }` with a `let` inside it");
-            diag.emit()
+        LetSource::IfLet => {
+            emit_diag!(
+                lint,
+                "`if let`",
+                "`if let` is useless",
+                "replacing the `if let` with a `let`"
+            );
         }
-        hir::MatchSource::IfLetGuardDesugar => {
-            let mut diag = lint.build("irrefutable `if let` guard pattern");
-            diag.note("this pattern will always match, so the guard is useless");
-            diag.help("consider removing the guard and adding a `let` inside the match arm");
-            diag.emit()
+        LetSource::IfLetGuard => {
+            emit_diag!(
+                lint,
+                "`if let` guard",
+                "guard is useless",
+                "removing the guard and adding a `let` inside the match arm"
+            );
         }
-        _ => {
-            bug!(
-                "expected `if let`, `while let`, or `if let` guard HIR match source, found {:?}",
-                source,
-            )
+        LetSource::WhileLet => {
+            emit_diag!(
+                lint,
+                "`while let`",
+                "loop will never exit",
+                "instead using a `loop { ... }` with a `let` inside it"
+            );
         }
     });
 }
 
-fn check_if_let_guard<'p, 'tcx>(
+fn check_let_reachability<'p, 'tcx>(
     cx: &mut MatchCheckCtxt<'p, 'tcx>,
-    pat: &'p super::Pat<'tcx>,
+    ls: LetSource,
     pat_id: HirId,
+    pat: &'p super::Pat<'tcx>,
+    span: Span,
 ) {
     let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
     let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
-    report_arm_reachability(&cx, &report, hir::MatchSource::IfLetGuardDesugar);
+
+    report_arm_reachability(&cx, &report, |arm_index, arm_span, arm_hir_id, _| {
+        match ls {
+            LetSource::IfLet | LetSource::WhileLet => {
+                match arm_index {
+                    // The arm with the user-specified pattern.
+                    0 => unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None),
+                    // The arm with the wildcard pattern.
+                    1 => irrefutable_let_pattern(pat_id, ls, arm_span, cx.tcx),
+                    _ => bug!(),
+                }
+            }
+            LetSource::IfLetGuard if arm_index == 0 => {
+                unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None);
+            }
+            _ => {}
+        }
+    });
 
     if report.non_exhaustiveness_witnesses.is_empty() {
         // The match is exhaustive, i.e. the `if let` pattern is irrefutable.
-        irrefutable_let_pattern(cx.tcx, pat.span, pat_id, hir::MatchSource::IfLetGuardDesugar)
+        irrefutable_let_pattern(pat_id, ls, span, cx.tcx);
     }
 }
 
 /// Report unreachable arms, if any.
-fn report_arm_reachability<'p, 'tcx>(
+fn report_arm_reachability<'p, 'tcx, F>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     report: &UsefulnessReport<'p, 'tcx>,
-    source: hir::MatchSource,
-) {
+    unreachable: F,
+) where
+    F: Fn(usize, Span, HirId, Option<Span>),
+{
     use Reachability::*;
     let mut catchall = None;
     for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
         match is_useful {
-            Unreachable => {
-                match source {
-                    hir::MatchSource::WhileDesugar => bug!(),
-
-                    hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
-                        // Check which arm we're on.
-                        match arm_index {
-                            // The arm with the user-specified pattern.
-                            0 => unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None),
-                            // The arm with the wildcard pattern.
-                            1 => irrefutable_let_pattern(cx.tcx, arm.pat.span, arm.hir_id, source),
-                            _ => bug!(),
-                        }
-                    }
-
-                    hir::MatchSource::IfLetGuardDesugar => {
-                        assert_eq!(arm_index, 0);
-                        unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None);
-                    }
-
-                    hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
-                        unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
-                    }
-
-                    // Unreachable patterns in try and await expressions occur when one of
-                    // the arms are an uninhabited type. Which is OK.
-                    hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
-                }
-            }
+            Unreachable => unreachable(arm_index, arm.pat.span, arm.hir_id, catchall),
             Reachable(unreachables) if unreachables.is_empty() => {}
             // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
             Reachable(unreachables) => {
@@ -723,3 +781,11 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_
         err.emit();
     }
 }
+
+#[derive(Clone, Copy, Debug)]
+pub enum LetSource {
+    GenericLet,
+    IfLet,
+    IfLetGuard,
+    WhileLet,
+}
diff --git a/compiler/rustc_mir_build/src/thir/visit.rs b/compiler/rustc_mir_build/src/thir/visit.rs
index ce5d4362c08..dda6c74d4de 100644
--- a/compiler/rustc_mir_build/src/thir/visit.rs
+++ b/compiler/rustc_mir_build/src/thir/visit.rs
@@ -57,6 +57,9 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
         Use { source } => visitor.visit_expr(&visitor.thir()[source]),
         NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]),
         Pointer { source, cast: _ } => visitor.visit_expr(&visitor.thir()[source]),
+        Let { expr, .. } => {
+            visitor.visit_expr(&visitor.thir()[expr]);
+        }
         Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
         Match { scrutinee, ref arms } => {
             visitor.visit_expr(&visitor.thir()[scrutinee]);