or-patterns: liveness: generalize + remove `top_pats_hack`.
diff --git a/src/librustc/hir/pat_util.rs b/src/librustc/hir/pat_util.rs
index 3d82f37..b68886b 100644
--- a/src/librustc/hir/pat_util.rs
+++ b/src/librustc/hir/pat_util.rs
@@ -77,6 +77,34 @@
         });
     }
 
+    /// Call `f` on every "binding" in a pattern, e.g., on `a` in
+    /// `match foo() { Some(a) => (), None => () }`.
+    ///
+    /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited.
+    pub fn each_binding_or_first<F>(&self, c: &mut F)
+        where F: FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident),
+    {
+        match &self.node {
+            PatKind::Binding(bm,  _, ident, sub) => {
+                c(*bm, self.hir_id, self.span, *ident);
+                sub.iter().for_each(|p| p.each_binding_or_first(c));
+            }
+            PatKind::Or(ps) => ps[0].each_binding_or_first(c),
+            PatKind::Struct(_, fs, _) => fs.iter().for_each(|f| f.pat.each_binding_or_first(c)),
+            PatKind::TupleStruct(_, ps, _) | PatKind::Tuple(ps, _) => {
+                ps.iter().for_each(|p| p.each_binding_or_first(c));
+            }
+            PatKind::Box(p) | PatKind::Ref(p, _) => p.each_binding_or_first(c),
+            PatKind::Slice(before, slice, after) => {
+                before.iter()
+                      .chain(slice.iter())
+                      .chain(after.iter())
+                      .for_each(|p| p.each_binding_or_first(c));
+            }
+            PatKind::Wild | PatKind::Lit(_) | PatKind::Range(..) | PatKind::Path(_) => {}
+        }
+    }
+
     /// Checks if the pattern contains any patterns that bind something to
     /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
     pub fn contains_bindings(&self) -> bool {
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 3c264f9..9afd147 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -96,7 +96,11 @@
 use self::LiveNodeKind::*;
 use self::VarKind::*;
 
+use crate::hir;
+use crate::hir::{Expr, HirId};
 use crate::hir::def::*;
+use crate::hir::def_id::DefId;
+use crate::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
 use crate::hir::Node;
 use crate::hir::ptr::P;
 use crate::ty::{self, TyCtxt};
@@ -105,20 +109,16 @@
 use crate::util::nodemap::{HirIdMap, HirIdSet};
 
 use errors::Applicability;
-use std::collections::{BTreeMap, VecDeque};
+use rustc_data_structures::fx::FxIndexMap;
+use std::collections::VecDeque;
 use std::{fmt, u32};
 use std::io::prelude::*;
 use std::io;
 use std::rc::Rc;
 use syntax::ast;
-use syntax::symbol::{kw, sym};
+use syntax::symbol::sym;
 use syntax_pos::Span;
 
-use crate::hir;
-use crate::hir::{Expr, HirId};
-use crate::hir::def_id::DefId;
-use crate::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
-
 #[derive(Copy, Clone, PartialEq)]
 struct Variable(u32);
 
@@ -727,35 +727,15 @@
         self.ir.variable(hir_id, span)
     }
 
-    fn pat_bindings<F>(&mut self, pat: &hir::Pat, mut f: F) where
-        F: FnMut(&mut Liveness<'a, 'tcx>, LiveNode, Variable, Span, HirId),
-    {
-        pat.each_binding(|_bm, hir_id, sp, n| {
-            let ln = self.live_node(hir_id, sp);
-            let var = self.variable(hir_id, n.span);
-            f(self, ln, var, n.span, hir_id);
-        })
-    }
-
-    fn arm_pats_bindings<F>(&mut self, pat: Option<&hir::Pat>, f: F) where
-        F: FnMut(&mut Liveness<'a, 'tcx>, LiveNode, Variable, Span, HirId),
-    {
-        if let Some(pat) = pat {
-            self.pat_bindings(pat, f);
-        }
-    }
-
-    fn define_bindings_in_pat(&mut self, pat: &hir::Pat, succ: LiveNode)
-                              -> LiveNode {
-        self.define_bindings_in_arm_pats(Some(pat), succ)
-    }
-
-    fn define_bindings_in_arm_pats(&mut self, pat: Option<&hir::Pat>, succ: LiveNode)
-                                   -> LiveNode {
-        let mut succ = succ;
-        self.arm_pats_bindings(pat, |this, ln, var, _sp, _id| {
-            this.init_from_succ(ln, succ);
-            this.define(ln, var);
+    fn define_bindings_in_pat(&mut self, pat: &hir::Pat, mut succ: LiveNode) -> LiveNode {
+        // In an or-pattern, only consider the first pattern; any later patterns
+        // must have the same bindings, and we also consider the first pattern
+        // to be the "authoritative" set of ids.
+        pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| {
+            let ln = self.live_node(hir_id, pat_sp);
+            let var = self.variable(hir_id, ident.span);
+            self.init_from_succ(ln, succ);
+            self.define(ln, var);
             succ = ln;
         });
         succ
@@ -1069,12 +1049,7 @@
                         arm.guard.as_ref().map(|hir::Guard::If(e)| &**e),
                         body_succ
                     );
-                    // only consider the first pattern; any later patterns must have
-                    // the same bindings, and we also consider the first pattern to be
-                    // the "authoritative" set of ids
-                    let arm_succ =
-                        self.define_bindings_in_arm_pats(arm.top_pats_hack().first().map(|p| &**p),
-                                                         guard_succ);
+                    let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
                     self.merge_from_succ(ln, arm_succ, first_merge);
                     first_merge = false;
                 };
@@ -1381,74 +1356,36 @@
         NestedVisitorMap::None
     }
 
-    fn visit_local(&mut self, l: &'tcx hir::Local) {
-        check_local(self, l);
+    fn visit_local(&mut self, local: &'tcx hir::Local) {
+        self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
+            if local.init.is_some() {
+                self.warn_about_dead_assign(spans, hir_id, ln, var);
+            }
+        });
+
+        intravisit::walk_local(self, local);
     }
+
     fn visit_expr(&mut self, ex: &'tcx Expr) {
         check_expr(self, ex);
     }
-    fn visit_arm(&mut self, a: &'tcx hir::Arm) {
-        check_arm(self, a);
+
+    fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
+        self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {});
+        intravisit::walk_arm(self, arm);
     }
 }
 
-fn check_local<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, local: &'tcx hir::Local) {
-    match local.init {
-        Some(_) => {
-            this.warn_about_unused_or_dead_vars_in_pat(&local.pat);
-        },
-        None => {
-            this.pat_bindings(&local.pat, |this, ln, var, sp, id| {
-                let span = local.pat.simple_ident().map_or(sp, |ident| ident.span);
-                this.warn_about_unused(vec![span], id, ln, var);
-            })
-        }
-    }
-
-    intravisit::walk_local(this, local);
-}
-
-fn check_arm<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, arm: &'tcx hir::Arm) {
-    // Only consider the variable from the first pattern; any later patterns must have
-    // the same bindings, and we also consider the first pattern to be the "authoritative" set of
-    // ids. However, we should take the spans of variables with the same name from the later
-    // patterns so the suggestions to prefix with underscores will apply to those too.
-    let mut vars: BTreeMap<String, (LiveNode, Variable, HirId, Vec<Span>)> = Default::default();
-
-    for pat in arm.top_pats_hack() {
-        this.arm_pats_bindings(Some(&*pat), |this, ln, var, sp, id| {
-            let name = this.ir.variable_name(var);
-            vars.entry(name)
-                .and_modify(|(.., spans)| {
-                    spans.push(sp);
-                })
-                .or_insert_with(|| {
-                    (ln, var, id, vec![sp])
-                });
-        });
-    }
-
-    for (_, (ln, var, id, spans)) in vars {
-        this.warn_about_unused(spans, id, ln, var);
-    }
-
-    intravisit::walk_arm(this, arm);
-}
-
-fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
+fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr) {
     match expr.node {
         hir::ExprKind::Assign(ref l, _) => {
             this.check_place(&l);
-
-            intravisit::walk_expr(this, expr);
         }
 
         hir::ExprKind::AssignOp(_, ref l, _) => {
             if !this.tables.is_method_call(expr) {
                 this.check_place(&l);
             }
-
-            intravisit::walk_expr(this, expr);
         }
 
         hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => {
@@ -1463,8 +1400,6 @@
                 }
                 this.visit_expr(output);
             }
-
-            intravisit::walk_expr(this, expr);
         }
 
         // no correctness conditions related to liveness
@@ -1477,13 +1412,13 @@
         hir::ExprKind::Lit(_) | hir::ExprKind::Block(..) | hir::ExprKind::AddrOf(..) |
         hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) |
         hir::ExprKind::Closure(..) | hir::ExprKind::Path(_) | hir::ExprKind::Yield(..) |
-        hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err => {
-            intravisit::walk_expr(this, expr);
-        }
+        hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err => {}
     }
+
+    intravisit::walk_expr(this, expr);
 }
 
-impl<'a, 'tcx> Liveness<'a, 'tcx> {
+impl<'tcx> Liveness<'_, 'tcx> {
     fn check_place(&mut self, expr: &'tcx Expr) {
         match expr.node {
             hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
@@ -1496,7 +1431,7 @@
                         // as being used.
                         let ln = self.live_node(expr.hir_id, expr.span);
                         let var = self.variable(var_hid, expr.span);
-                        self.warn_about_dead_assign(expr.span, expr.hir_id, ln, var);
+                        self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
                     }
                 }
             }
@@ -1518,109 +1453,112 @@
     }
 
     fn warn_about_unused_args(&self, body: &hir::Body, entry_ln: LiveNode) {
-        for param in &body.params {
-            param.pat.each_binding(|_bm, hir_id, _, ident| {
-                let sp = ident.span;
-                let var = self.variable(hir_id, sp);
-                // Ignore unused self.
-                if ident.name != kw::SelfLower {
-                    if !self.warn_about_unused(vec![sp], hir_id, entry_ln, var) {
-                        if self.live_on_entry(entry_ln, var).is_none() {
-                            self.report_dead_assign(hir_id, sp, var, true);
-                        }
-                    }
+        for p in &body.params {
+            self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
+                if self.live_on_entry(ln, var).is_none() {
+                    self.report_dead_assign(hir_id, spans, var, true);
                 }
-            })
+            });
         }
     }
 
-    fn warn_about_unused_or_dead_vars_in_pat(&mut self, pat: &hir::Pat) {
-        self.pat_bindings(pat, |this, ln, var, sp, id| {
-            if !this.warn_about_unused(vec![sp], id, ln, var) {
-                this.warn_about_dead_assign(sp, id, ln, var);
+    fn check_unused_vars_in_pat(
+        &self,
+        pat: &hir::Pat,
+        entry_ln: Option<LiveNode>,
+        on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable),
+    ) {
+        // In an or-pattern, only consider the variable; any later patterns must have the same
+        // bindings, and we also consider the first pattern to be the "authoritative" set of ids.
+        // However, we should take the spans of variables with the same name from the later
+        // patterns so the suggestions to prefix with underscores will apply to those too.
+        let mut vars: FxIndexMap<String, (LiveNode, Variable, HirId, Vec<Span>)> = <_>::default();
+
+        pat.each_binding(|_, hir_id, pat_sp, ident| {
+            let ln = entry_ln.unwrap_or_else(|| self.live_node(hir_id, pat_sp));
+            let var = self.variable(hir_id, ident.span);
+            vars.entry(self.ir.variable_name(var))
+                .and_modify(|(.., spans)| spans.push(ident.span))
+                .or_insert_with(|| (ln, var, hir_id, vec![ident.span]));
+        });
+
+        for (_, (ln, var, id, spans)) in vars {
+            if self.used_on_entry(ln, var) {
+                on_used_on_entry(spans, id, ln, var);
+            } else {
+                self.report_unused(spans, id, ln, var);
             }
-        })
+        }
     }
 
-    fn warn_about_unused(&self,
-                         spans: Vec<Span>,
-                         hir_id: HirId,
-                         ln: LiveNode,
-                         var: Variable)
-                         -> bool {
-        if !self.used_on_entry(ln, var) {
-            let r = self.should_warn(var);
-            if let Some(name) = r {
-                // annoying: for parameters in funcs like `fn(x: i32)
-                // {ret}`, there is only one node, so asking about
-                // assigned_on_exit() is not meaningful.
-                let is_assigned = if ln == self.s.exit_ln {
-                    false
-                } else {
-                    self.assigned_on_exit(ln, var).is_some()
-                };
+    fn report_unused(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
+        if let Some(name) = self.should_warn(var).filter(|name| name != "self") {
+            // annoying: for parameters in funcs like `fn(x: i32)
+            // {ret}`, there is only one node, so asking about
+            // assigned_on_exit() is not meaningful.
+            let is_assigned = if ln == self.s.exit_ln {
+                false
+            } else {
+                self.assigned_on_exit(ln, var).is_some()
+            };
 
-                if is_assigned {
-                    self.ir.tcx.lint_hir_note(
-                        lint::builtin::UNUSED_VARIABLES,
-                        hir_id,
-                        spans,
-                        &format!("variable `{}` is assigned to, but never used", name),
-                        &format!("consider using `_{}` instead", name),
-                    );
-                } else if name != "self" {
-                    let mut err = self.ir.tcx.struct_span_lint_hir(
-                        lint::builtin::UNUSED_VARIABLES,
-                        hir_id,
-                        spans.clone(),
-                        &format!("unused variable: `{}`", name),
-                    );
+            if is_assigned {
+                self.ir.tcx.lint_hir_note(
+                    lint::builtin::UNUSED_VARIABLES,
+                    hir_id,
+                    spans,
+                    &format!("variable `{}` is assigned to, but never used", name),
+                    &format!("consider using `_{}` instead", name),
+                );
+            } else {
+                let mut err = self.ir.tcx.struct_span_lint_hir(
+                    lint::builtin::UNUSED_VARIABLES,
+                    hir_id,
+                    spans.clone(),
+                    &format!("unused variable: `{}`", name),
+                );
 
-                    if self.ir.variable_is_shorthand(var) {
-                        if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
-                            // Handle `ref` and `ref mut`.
-                            let spans = spans.iter()
-                                .map(|_span| (pat.span, format!("{}: _", name)))
-                                .collect();
+                if self.ir.variable_is_shorthand(var) {
+                    if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
+                        // Handle `ref` and `ref mut`.
+                        let spans = spans.iter()
+                            .map(|_span| (pat.span, format!("{}: _", name)))
+                            .collect();
 
-                            err.multipart_suggestion(
-                                "try ignoring the field",
-                                spans,
-                                Applicability::MachineApplicable,
-                            );
-                        }
-                    } else {
                         err.multipart_suggestion(
-                            "consider prefixing with an underscore",
-                            spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
+                            "try ignoring the field",
+                            spans,
                             Applicability::MachineApplicable,
                         );
                     }
-
-                    err.emit()
+                } else {
+                    err.multipart_suggestion(
+                        "consider prefixing with an underscore",
+                        spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
+                        Applicability::MachineApplicable,
+                    );
                 }
+
+                err.emit()
             }
-            true
-        } else {
-            false
         }
     }
 
-    fn warn_about_dead_assign(&self, sp: Span, hir_id: HirId, ln: LiveNode, var: Variable) {
+    fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
         if self.live_on_exit(ln, var).is_none() {
-            self.report_dead_assign(hir_id, sp, var, false);
+            self.report_dead_assign(hir_id, spans, var, false);
         }
     }
 
-    fn report_dead_assign(&self, hir_id: HirId, sp: Span, var: Variable, is_argument: bool) {
+    fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_argument: bool) {
         if let Some(name) = self.should_warn(var) {
             if is_argument {
-                self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, sp,
+                self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans,
                 &format!("value passed to `{}` is never read", name))
                 .help("maybe it is overwritten before being read?")
                 .emit();
             } else {
-                self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, sp,
+                self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans,
                 &format!("value assigned to `{}` is never read", name))
                 .help("maybe it is overwritten before being read?")
                 .emit();