about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-07-02 16:33:00 +0000
committerbors <bors@rust-lang.org>2020-07-02 16:33:00 +0000
commit3503f565e1fb7296983757d2716346f48a4a262b (patch)
tree3a1a62662e67ccb5eb38100e6e428d6ed747d801
parent8a6d4342be6a6acbade8e7ef65e73d27ee8c9144 (diff)
parent4b2d9e60f41c11238d60ee9ca319136f61e72ca1 (diff)
downloadrust-3503f565e1fb7296983757d2716346f48a4a262b.tar.gz
rust-3503f565e1fb7296983757d2716346f48a4a262b.zip
Auto merge of #73751 - eddyb:no-empty-tables, r=nikomatsakis
Remove `TypeckTables::empty(None)` and make hir_owner non-optional.

Each commit before the last one removes uses of `TypeckTables::empty(None)`, replacing the empty tables with having `Option` around the `&'tcx TypeckTables<'tcx>` that HIR visitors kept track of.

The last commit removes the concept of "empty `TypeckTables`" altogether, guaranteeing that every `TypeckTables` corresponds to a HIR body owner.

r? @nikomatsakis
-rw-r--r--src/librustc_driver/lib.rs1
-rw-r--r--src/librustc_driver/pretty.rs29
-rw-r--r--src/librustc_infer/infer/error_reporting/mod.rs2
-rw-r--r--src/librustc_infer/infer/mod.rs2
-rw-r--r--src/librustc_lint/builtin.rs9
-rw-r--r--src/librustc_lint/context.rs41
-rw-r--r--src/librustc_lint/late.rs2
-rw-r--r--src/librustc_lint/lib.rs1
-rw-r--r--src/librustc_lint/unused.rs2
-rw-r--r--src/librustc_middle/ty/context.rs69
-rw-r--r--src/librustc_passes/dead.rs45
-rw-r--r--src/librustc_passes/lib.rs1
-rw-r--r--src/librustc_passes/reachable.rs27
-rw-r--r--src/librustc_privacy/lib.rs156
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs18
-rw-r--r--src/librustc_save_analysis/lib.rs36
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/suggestions.rs2
-rw-r--r--src/librustc_typeck/check/method/suggest.rs127
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/librustc_typeck/check/writeback.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/atomic_ordering.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/default_trait_access.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/eval_order_dependence.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_return.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/let_and_return.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/map_unit_fn.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_discriminant.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/regex.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/temporary_assignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/to_digit_is_some.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/unnamed_address.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_conversion.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/higher.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/hir_utils.rs46
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs8
47 files changed, 355 insertions, 371 deletions
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index b45ab0f80ff..c2c19b6b405 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -6,6 +6,7 @@
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
 #![feature(nll)]
+#![cfg_attr(bootstrap, feature(track_caller))]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index 0a21eb8de05..6a34a310f73 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -80,8 +80,7 @@ where
         PpmTyped => {
             abort_on_err(tcx.analysis(LOCAL_CRATE), tcx.sess);
 
-            let empty_tables = ty::TypeckTables::empty(None);
-            let annotation = TypedAnnotation { tcx, tables: Cell::new(&empty_tables) };
+            let annotation = TypedAnnotation { tcx, maybe_typeck_tables: Cell::new(None) };
             tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir().krate()))
         }
         _ => panic!("Should use call_with_pp_support"),
@@ -304,12 +303,22 @@ impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
     }
 }
 
-struct TypedAnnotation<'a, 'tcx> {
+struct TypedAnnotation<'tcx> {
     tcx: TyCtxt<'tcx>,
-    tables: Cell<&'a ty::TypeckTables<'tcx>>,
+    maybe_typeck_tables: Cell<Option<&'tcx ty::TypeckTables<'tcx>>>,
 }
 
-impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> {
+impl<'tcx> TypedAnnotation<'tcx> {
+    /// Gets the type-checking side-tables for the current body.
+    /// As this will ICE if called outside bodies, only call when working with
+    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
+    #[track_caller]
+    fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
+        self.maybe_typeck_tables.get().expect("`TypedAnnotation::tables` called outside of body")
+    }
+}
+
+impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> {
     fn sess(&self) -> &Session {
         &self.tcx.sess
     }
@@ -327,15 +336,15 @@ impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> {
+impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> {
     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
-        let old_tables = self.tables.get();
+        let old_maybe_typeck_tables = self.maybe_typeck_tables.get();
         if let pprust_hir::Nested::Body(id) = nested {
-            self.tables.set(self.tcx.body_tables(id));
+            self.maybe_typeck_tables.set(Some(self.tcx.body_tables(id)));
         }
         let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>);
         pprust_hir::PpAnn::nested(pp_ann, state, nested);
-        self.tables.set(old_tables);
+        self.maybe_typeck_tables.set(old_maybe_typeck_tables);
     }
     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
         if let pprust_hir::AnnNode::Expr(_) = node {
@@ -347,7 +356,7 @@ impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> {
             s.s.space();
             s.s.word("as");
             s.s.space();
-            s.s.word(self.tables.get().expr_ty(expr).to_string());
+            s.s.word(self.tables().expr_ty(expr).to_string());
             s.pclose();
         }
     }
diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs
index 7fdcbd31df3..b1ea2223707 100644
--- a/src/librustc_infer/infer/error_reporting/mod.rs
+++ b/src/librustc_infer/infer/error_reporting/mod.rs
@@ -1684,7 +1684,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         // Attempt to obtain the span of the parameter so we can
         // suggest adding an explicit lifetime bound to it.
         let generics =
-            self.in_progress_tables.and_then(|table| table.borrow().hir_owner).map(|table_owner| {
+            self.in_progress_tables.map(|table| table.borrow().hir_owner).map(|table_owner| {
                 let hir_id = hir.as_local_hir_id(table_owner);
                 let parent_id = hir.get_parent_item(hir_id);
                 (
diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs
index 76ac61c0672..27da514a17f 100644
--- a/src/librustc_infer/infer/mod.rs
+++ b/src/librustc_infer/infer/mod.rs
@@ -588,7 +588,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
     /// Used only by `rustc_typeck` during body type-checking/inference,
     /// will initialize `in_progress_tables` with fresh `TypeckTables`.
     pub fn with_fresh_in_progress_tables(mut self, table_owner: LocalDefId) -> Self {
-        self.fresh_tables = Some(RefCell::new(ty::TypeckTables::empty(Some(table_owner))));
+        self.fresh_tables = Some(RefCell::new(ty::TypeckTables::new(table_owner)));
         self
     }
 
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index d55cbbe19c2..77d403696f1 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -165,7 +165,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
                 .pat_ty(pat)
                 .ty_adt_def()
                 .expect("struct pattern type is not an ADT")
-                .variant_of_res(cx.tables().qpath_res(qpath, pat.hir_id));
+                .variant_of_res(cx.qpath_res(qpath, pat.hir_id));
             for fieldpat in field_pats {
                 if fieldpat.is_shorthand {
                     continue;
@@ -901,7 +901,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes {
             expr: &hir::Expr<'_>,
         ) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
             let def = if let hir::ExprKind::Path(ref qpath) = expr.kind {
-                cx.tables().qpath_res(qpath, expr.hir_id)
+                cx.qpath_res(qpath, expr.hir_id)
             } else {
                 return None;
             };
@@ -1891,7 +1891,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
             if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind {
                 // Find calls to `mem::{uninitialized,zeroed}` methods.
                 if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
-                    let def_id = cx.tables().qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
+                    let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
 
                     if cx.tcx.is_diagnostic_item(sym::mem_zeroed, def_id) {
                         return Some(InitKind::Zeroed);
@@ -1911,8 +1911,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
                     // See if the `self` parameter is one of the dangerous constructors.
                     if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind {
                         if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
-                            let def_id =
-                                cx.tables().qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
+                            let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
 
                             if cx.tcx.is_diagnostic_item(sym::maybe_uninit_zeroed, def_id) {
                                 return Some(InitKind::Zeroed);
diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs
index 0b4ae131af0..a2f9c9ea7cb 100644
--- a/src/librustc_lint/context.rs
+++ b/src/librustc_lint/context.rs
@@ -24,6 +24,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync;
 use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
+use rustc_hir::def::Res;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
 use rustc_middle::lint::LintDiagnosticBuilder;
@@ -427,15 +428,12 @@ pub struct LateContext<'a, 'tcx> {
     /// Current body, or `None` if outside a body.
     pub enclosing_body: Option<hir::BodyId>,
 
-    /// Type-checking side-tables for the current body. Access using the
-    /// `tables` method, which handles querying the tables on demand.
+    /// Type-checking side-tables for the current body. Access using the `tables`
+    /// and `maybe_tables` methods, which handle querying the tables on demand.
     // FIXME(eddyb) move all the code accessing internal fields like this,
     // to this module, to avoid exposing it to lint logic.
     pub(super) cached_typeck_tables: Cell<Option<&'tcx ty::TypeckTables<'tcx>>>,
 
-    // HACK(eddyb) replace this with having `Option` around `&TypeckTables`.
-    pub(super) empty_typeck_tables: &'a ty::TypeckTables<'tcx>,
-
     /// Parameter environment for the item we are in.
     pub param_env: ty::ParamEnv<'tcx>,
 
@@ -677,18 +675,35 @@ impl LintContext for EarlyContext<'_> {
 
 impl<'a, 'tcx> LateContext<'a, 'tcx> {
     /// Gets the type-checking side-tables for the current body,
-    /// or empty `TypeckTables` if outside a body.
-    // FIXME(eddyb) return `Option<&'tcx ty::TypeckTables<'tcx>>`,
-    // where `None` indicates we're outside a body.
-    pub fn tables(&self) -> &'a ty::TypeckTables<'tcx> {
-        if let Some(body) = self.enclosing_body {
-            self.cached_typeck_tables.get().unwrap_or_else(|| {
+    /// or `None` if outside a body.
+    pub fn maybe_typeck_tables(&self) -> Option<&'tcx ty::TypeckTables<'tcx>> {
+        self.cached_typeck_tables.get().or_else(|| {
+            self.enclosing_body.map(|body| {
                 let tables = self.tcx.body_tables(body);
                 self.cached_typeck_tables.set(Some(tables));
                 tables
             })
-        } else {
-            self.empty_typeck_tables
+        })
+    }
+
+    /// Gets the type-checking side-tables for the current body.
+    /// As this will ICE if called outside bodies, only call when working with
+    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
+    #[track_caller]
+    pub fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
+        self.maybe_typeck_tables().expect("`LateContext::tables` called outside of body")
+    }
+
+    /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable.
+    /// Unlike `.tables().qpath_res(qpath, id)`, this can be used even outside
+    /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing.
+    pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
+        match *qpath {
+            hir::QPath::Resolved(_, ref path) => path.res,
+            hir::QPath::TypeRelative(..) => self
+                .maybe_typeck_tables()
+                .and_then(|tables| tables.type_dependent_def(id))
+                .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
         }
     }
 
diff --git a/src/librustc_lint/late.rs b/src/librustc_lint/late.rs
index ef62d44c402..9fe7edf6cca 100644
--- a/src/librustc_lint/late.rs
+++ b/src/librustc_lint/late.rs
@@ -378,7 +378,6 @@ fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(
         tcx,
         enclosing_body: None,
         cached_typeck_tables: Cell::new(None),
-        empty_typeck_tables: &ty::TypeckTables::empty(None),
         param_env: ty::ParamEnv::empty(),
         access_levels,
         lint_store: unerased_lint_store(tcx),
@@ -427,7 +426,6 @@ fn late_lint_pass_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tc
         tcx,
         enclosing_body: None,
         cached_typeck_tables: Cell::new(None),
-        empty_typeck_tables: &ty::TypeckTables::empty(None),
         param_env: ty::ParamEnv::empty(),
         access_levels,
         lint_store: unerased_lint_store(tcx),
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 4da98d20159..45a86cf2cc6 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -34,6 +34,7 @@
 #![feature(never_type)]
 #![feature(nll)]
 #![feature(or_patterns)]
+#![cfg_attr(bootstrap, feature(track_caller))]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 506ac77894a..bf73ea46d5e 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -55,7 +55,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
             hir::ExprKind::Call(ref callee, _) => {
                 match callee.kind {
                     hir::ExprKind::Path(ref qpath) => {
-                        match cx.tables().qpath_res(qpath, callee.hir_id) {
+                        match cx.qpath_res(qpath, callee.hir_id) {
                             Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id),
                             // `Res::Local` if it was a closure, for which we
                             // do not currently support must-use linting
diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs
index cee4efaa465..d942c07965a 100644
--- a/src/librustc_middle/ty/context.rs
+++ b/src/librustc_middle/ty/context.rs
@@ -188,7 +188,7 @@ pub struct CommonConsts<'tcx> {
 }
 
 pub struct LocalTableInContext<'a, V> {
-    hir_owner: Option<LocalDefId>,
+    hir_owner: LocalDefId,
     data: &'a ItemLocalMap<V>,
 }
 
@@ -199,42 +199,27 @@ pub struct LocalTableInContext<'a, V> {
 /// would be in a different frame of reference and using its `local_id`
 /// would result in lookup errors, or worse, in silently wrong data being
 /// stored/returned.
-fn validate_hir_id_for_typeck_tables(
-    hir_owner: Option<LocalDefId>,
-    hir_id: hir::HirId,
-    mut_access: bool,
-) {
-    if let Some(hir_owner) = hir_owner {
-        if hir_id.owner != hir_owner {
-            ty::tls::with(|tcx| {
-                bug!(
-                    "node {} with HirId::owner {:?} cannot be placed in TypeckTables with hir_owner {:?}",
-                    tcx.hir().node_to_string(hir_id),
-                    hir_id.owner,
-                    hir_owner
-                )
-            });
-        }
-    } else {
-        // We use "Null Object" TypeckTables in some of the analysis passes.
-        // These are just expected to be empty and their `hir_owner` is
-        // `None`. Therefore we cannot verify whether a given `HirId` would
-        // be a valid key for the given table. Instead we make sure that
-        // nobody tries to write to such a Null Object table.
-        if mut_access {
-            bug!("access to invalid TypeckTables")
-        }
+fn validate_hir_id_for_typeck_tables(hir_owner: LocalDefId, hir_id: hir::HirId) {
+    if hir_id.owner != hir_owner {
+        ty::tls::with(|tcx| {
+            bug!(
+                "node {} with HirId::owner {:?} cannot be placed in TypeckTables with hir_owner {:?}",
+                tcx.hir().node_to_string(hir_id),
+                hir_id.owner,
+                hir_owner
+            )
+        });
     }
 }
 
 impl<'a, V> LocalTableInContext<'a, V> {
     pub fn contains_key(&self, id: hir::HirId) -> bool {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, false);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.data.contains_key(&id.local_id)
     }
 
     pub fn get(&self, id: hir::HirId) -> Option<&V> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, false);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.data.get(&id.local_id)
     }
 
@@ -252,28 +237,28 @@ impl<'a, V> ::std::ops::Index<hir::HirId> for LocalTableInContext<'a, V> {
 }
 
 pub struct LocalTableInContextMut<'a, V> {
-    hir_owner: Option<LocalDefId>,
+    hir_owner: LocalDefId,
     data: &'a mut ItemLocalMap<V>,
 }
 
 impl<'a, V> LocalTableInContextMut<'a, V> {
     pub fn get_mut(&mut self, id: hir::HirId) -> Option<&mut V> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, true);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.data.get_mut(&id.local_id)
     }
 
     pub fn entry(&mut self, id: hir::HirId) -> Entry<'_, hir::ItemLocalId, V> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, true);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.data.entry(id.local_id)
     }
 
     pub fn insert(&mut self, id: hir::HirId, val: V) -> Option<V> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, true);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.data.insert(id.local_id, val)
     }
 
     pub fn remove(&mut self, id: hir::HirId) -> Option<V> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, true);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.data.remove(&id.local_id)
     }
 }
@@ -324,7 +309,7 @@ pub struct GeneratorInteriorTypeCause<'tcx> {
 #[derive(RustcEncodable, RustcDecodable, Debug)]
 pub struct TypeckTables<'tcx> {
     /// The `HirId::owner` all `ItemLocalId`s in this table are relative to.
-    pub hir_owner: Option<LocalDefId>,
+    pub hir_owner: LocalDefId,
 
     /// Resolved definitions for `<T>::X` associated paths and
     /// method calls, including those of overloaded operators.
@@ -432,7 +417,7 @@ pub struct TypeckTables<'tcx> {
 }
 
 impl<'tcx> TypeckTables<'tcx> {
-    pub fn empty(hir_owner: Option<LocalDefId>) -> TypeckTables<'tcx> {
+    pub fn new(hir_owner: LocalDefId) -> TypeckTables<'tcx> {
         TypeckTables {
             hir_owner,
             type_dependent_defs: Default::default(),
@@ -474,7 +459,7 @@ impl<'tcx> TypeckTables<'tcx> {
     }
 
     pub fn type_dependent_def(&self, id: HirId) -> Option<(DefKind, DefId)> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, false);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.type_dependent_defs.get(&id.local_id).cloned().and_then(|r| r.ok())
     }
 
@@ -521,7 +506,7 @@ impl<'tcx> TypeckTables<'tcx> {
     }
 
     pub fn node_type_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, false);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.node_types.get(&id.local_id).cloned()
     }
 
@@ -530,12 +515,12 @@ impl<'tcx> TypeckTables<'tcx> {
     }
 
     pub fn node_substs(&self, id: hir::HirId) -> SubstsRef<'tcx> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, false);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.node_substs.get(&id.local_id).cloned().unwrap_or_else(|| InternalSubsts::empty())
     }
 
     pub fn node_substs_opt(&self, id: hir::HirId) -> Option<SubstsRef<'tcx>> {
-        validate_hir_id_for_typeck_tables(self.hir_owner, id, false);
+        validate_hir_id_for_typeck_tables(self.hir_owner, id);
         self.node_substs.get(&id.local_id).cloned()
     }
 
@@ -578,7 +563,7 @@ impl<'tcx> TypeckTables<'tcx> {
     }
 
     pub fn expr_adjustments(&self, expr: &hir::Expr<'_>) -> &[ty::adjustment::Adjustment<'tcx>] {
-        validate_hir_id_for_typeck_tables(self.hir_owner, expr.hir_id, false);
+        validate_hir_id_for_typeck_tables(self.hir_owner, expr.hir_id);
         self.adjustments.get(&expr.hir_id.local_id).map_or(&[], |a| &a[..])
     }
 
@@ -657,7 +642,7 @@ impl<'tcx> TypeckTables<'tcx> {
     }
 
     pub fn is_coercion_cast(&self, hir_id: hir::HirId) -> bool {
-        validate_hir_id_for_typeck_tables(self.hir_owner, hir_id, true);
+        validate_hir_id_for_typeck_tables(self.hir_owner, hir_id);
         self.coercion_casts.contains(&hir_id.local_id)
     }
 
@@ -710,7 +695,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckTables<'tcx> {
             hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| {
                 let ty::UpvarId { var_path, closure_expr_id } = *up_var_id;
 
-                assert_eq!(Some(var_path.hir_id.owner), hir_owner);
+                assert_eq!(var_path.hir_id.owner, hir_owner);
 
                 (
                     hcx.local_def_path_hash(var_path.hir_id.owner),
diff --git a/src/librustc_passes/dead.rs b/src/librustc_passes/dead.rs
index 503fbb64db8..87348894a5a 100644
--- a/src/librustc_passes/dead.rs
+++ b/src/librustc_passes/dead.rs
@@ -37,10 +37,10 @@ fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
     }
 }
 
-struct MarkSymbolVisitor<'a, 'tcx> {
+struct MarkSymbolVisitor<'tcx> {
     worklist: Vec<hir::HirId>,
     tcx: TyCtxt<'tcx>,
-    tables: &'a ty::TypeckTables<'tcx>,
+    maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>,
     live_symbols: FxHashSet<hir::HirId>,
     repr_has_repr_c: bool,
     in_pat: bool,
@@ -50,7 +50,15 @@ struct MarkSymbolVisitor<'a, 'tcx> {
     struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
 }
 
-impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
+impl<'tcx> MarkSymbolVisitor<'tcx> {
+    /// Gets the type-checking side-tables for the current body.
+    /// As this will ICE if called outside bodies, only call when working with
+    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
+    #[track_caller]
+    fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
+        self.maybe_typeck_tables.expect("`MarkSymbolVisitor::tables` called outside of body")
+    }
+
     fn check_def_id(&mut self, def_id: DefId) {
         if let Some(def_id) = def_id.as_local() {
             let hir_id = self.tcx.hir().as_local_hir_id(def_id);
@@ -107,7 +115,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
     }
 
     fn lookup_and_handle_method(&mut self, id: hir::HirId) {
-        if let Some(def_id) = self.tables.type_dependent_def_id(id) {
+        if let Some(def_id) = self.tables().type_dependent_def_id(id) {
             self.check_def_id(def_id);
         } else {
             bug!("no type-dependent def for method");
@@ -115,9 +123,9 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
     }
 
     fn handle_field_access(&mut self, lhs: &hir::Expr<'_>, hir_id: hir::HirId) {
-        match self.tables.expr_ty_adjusted(lhs).kind {
+        match self.tables().expr_ty_adjusted(lhs).kind {
             ty::Adt(def, _) => {
-                let index = self.tcx.field_index(hir_id, self.tables);
+                let index = self.tcx.field_index(hir_id, self.tables());
                 self.insert_def_id(def.non_enum_variant().fields[index].did);
             }
             ty::Tuple(..) => {}
@@ -131,7 +139,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
         res: Res,
         pats: &[hir::FieldPat<'_>],
     ) {
-        let variant = match self.tables.node_type(lhs.hir_id).kind {
+        let variant = match self.tables().node_type(lhs.hir_id).kind {
             ty::Adt(adt, _) => adt.variant_of_res(res),
             _ => span_bug!(lhs.span, "non-ADT in struct pattern"),
         };
@@ -139,7 +147,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
             if let PatKind::Wild = pat.pat.kind {
                 continue;
             }
-            let index = self.tcx.field_index(pat.hir_id, self.tables);
+            let index = self.tcx.field_index(pat.hir_id, self.tables());
             self.insert_def_id(variant.fields[index].did);
         }
     }
@@ -204,14 +212,14 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
     fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &[hir::Field<'_>]) {
         if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() {
             for field in fields {
-                let index = self.tcx.field_index(field.hir_id, self.tables);
+                let index = self.tcx.field_index(field.hir_id, self.tables());
                 self.insert_def_id(adt.non_enum_variant().fields[index].did);
             }
         }
     }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
     type Map = intravisit::ErasedMap<'tcx>;
 
     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
@@ -219,11 +227,10 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
     }
 
     fn visit_nested_body(&mut self, body: hir::BodyId) {
-        let old_tables = self.tables;
-        self.tables = self.tcx.body_tables(body);
+        let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body));
         let body = self.tcx.hir().body(body);
         self.visit_body(body);
-        self.tables = old_tables;
+        self.maybe_typeck_tables = old_maybe_typeck_tables;
     }
 
     fn visit_variant_data(
@@ -248,7 +255,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
         match expr.kind {
             hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => {
-                let res = self.tables.qpath_res(qpath, expr.hir_id);
+                let res = self.tables().qpath_res(qpath, expr.hir_id);
                 self.handle_res(res);
             }
             hir::ExprKind::MethodCall(..) => {
@@ -258,9 +265,9 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
                 self.handle_field_access(&lhs, expr.hir_id);
             }
             hir::ExprKind::Struct(ref qpath, ref fields, _) => {
-                let res = self.tables.qpath_res(qpath, expr.hir_id);
+                let res = self.tables().qpath_res(qpath, expr.hir_id);
                 self.handle_res(res);
-                if let ty::Adt(ref adt, _) = self.tables.expr_ty(expr).kind {
+                if let ty::Adt(ref adt, _) = self.tables().expr_ty(expr).kind {
                     self.mark_as_used_if_union(adt, fields);
                 }
             }
@@ -283,11 +290,11 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
     fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
         match pat.kind {
             PatKind::Struct(ref path, ref fields, _) => {
-                let res = self.tables.qpath_res(path, pat.hir_id);
+                let res = self.tables().qpath_res(path, pat.hir_id);
                 self.handle_field_pattern_match(pat, res, fields);
             }
             PatKind::Path(ref qpath) => {
-                let res = self.tables.qpath_res(qpath, pat.hir_id);
+                let res = self.tables().qpath_res(qpath, pat.hir_id);
                 self.handle_res(res);
             }
             _ => (),
@@ -473,7 +480,7 @@ fn find_live<'tcx>(
     let mut symbol_visitor = MarkSymbolVisitor {
         worklist,
         tcx,
-        tables: &ty::TypeckTables::empty(None),
+        maybe_typeck_tables: None,
         live_symbols: Default::default(),
         repr_has_repr_c: false,
         in_pat: false,
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index b55c5ec47ee..5109d8debee 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -8,6 +8,7 @@
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
 #![feature(or_patterns)]
+#![cfg_attr(bootstrap, feature(track_caller))]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/src/librustc_passes/reachable.rs b/src/librustc_passes/reachable.rs
index c9a4428c007..50fcefa3569 100644
--- a/src/librustc_passes/reachable.rs
+++ b/src/librustc_passes/reachable.rs
@@ -60,10 +60,10 @@ fn method_might_be_inlined(
 }
 
 // Information needed while computing reachability.
-struct ReachableContext<'a, 'tcx> {
+struct ReachableContext<'tcx> {
     // The type context.
     tcx: TyCtxt<'tcx>,
-    tables: &'a ty::TypeckTables<'tcx>,
+    maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>,
     // The set of items which must be exported in the linkage sense.
     reachable_symbols: HirIdSet,
     // A worklist of item IDs. Each item ID in this worklist will be inlined
@@ -73,7 +73,7 @@ struct ReachableContext<'a, 'tcx> {
     any_library: bool,
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
     type Map = intravisit::ErasedMap<'tcx>;
 
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -81,18 +81,17 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> {
     }
 
     fn visit_nested_body(&mut self, body: hir::BodyId) {
-        let old_tables = self.tables;
-        self.tables = self.tcx.body_tables(body);
+        let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body));
         let body = self.tcx.hir().body(body);
         self.visit_body(body);
-        self.tables = old_tables;
+        self.maybe_typeck_tables = old_maybe_typeck_tables;
     }
 
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
         let res = match expr.kind {
-            hir::ExprKind::Path(ref qpath) => Some(self.tables.qpath_res(qpath, expr.hir_id)),
+            hir::ExprKind::Path(ref qpath) => Some(self.tables().qpath_res(qpath, expr.hir_id)),
             hir::ExprKind::MethodCall(..) => self
-                .tables
+                .tables()
                 .type_dependent_def(expr.hir_id)
                 .map(|(kind, def_id)| Res::Def(kind, def_id)),
             _ => None,
@@ -133,7 +132,15 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
+impl<'tcx> ReachableContext<'tcx> {
+    /// Gets the type-checking side-tables for the current body.
+    /// As this will ICE if called outside bodies, only call when working with
+    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
+    #[track_caller]
+    fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
+        self.maybe_typeck_tables.expect("`ReachableContext::tables` called outside of body")
+    }
+
     // Returns true if the given def ID represents a local item that is
     // eligible for inlining and false otherwise.
     fn def_id_represents_local_inlined_item(&self, def_id: DefId) -> bool {
@@ -381,7 +388,7 @@ fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, crate_num: CrateNum) -> &'tcx HirIdSet
         });
     let mut reachable_context = ReachableContext {
         tcx,
-        tables: &ty::TypeckTables::empty(None),
+        maybe_typeck_tables: None,
         reachable_symbols: Default::default(),
         worklist: Vec::new(),
         any_library,
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 20f09ef52f0..de21365c536 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -2,6 +2,7 @@
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
 #![feature(or_patterns)]
+#![cfg_attr(bootstrap, feature(track_caller))]
 #![recursion_limit = "256"]
 
 use rustc_attr as attr;
@@ -345,17 +346,6 @@ fn def_id_visibility<'tcx>(
     }
 }
 
-// Set the correct `TypeckTables` for the given `item_id` (or an empty table if
-// there is no `TypeckTables` for the item).
-fn item_tables<'a, 'tcx>(
-    tcx: TyCtxt<'tcx>,
-    hir_id: hir::HirId,
-    empty_tables: &'a ty::TypeckTables<'tcx>,
-) -> &'a ty::TypeckTables<'tcx> {
-    let def_id = tcx.hir().local_def_id(hir_id);
-    if tcx.has_typeck_tables(def_id) { tcx.typeck_tables_of(def_id) } else { empty_tables }
-}
-
 fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visibility {
     if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 }
 }
@@ -1029,14 +1019,21 @@ impl DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
 /// This pass performs remaining checks for fields in struct expressions and patterns.
 //////////////////////////////////////////////////////////////////////////////////////
 
-struct NamePrivacyVisitor<'a, 'tcx> {
+struct NamePrivacyVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
-    tables: &'a ty::TypeckTables<'tcx>,
+    maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>,
     current_item: Option<hir::HirId>,
-    empty_tables: &'a ty::TypeckTables<'tcx>,
 }
 
-impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
+impl<'tcx> NamePrivacyVisitor<'tcx> {
+    /// Gets the type-checking side-tables for the current body.
+    /// As this will ICE if called outside bodies, only call when working with
+    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
+    #[track_caller]
+    fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
+        self.maybe_typeck_tables.expect("`NamePrivacyVisitor::tables` called outside of body")
+    }
+
     // Checks that a field in a struct constructor (expression or pattern) is accessible.
     fn check_field(
         &mut self,
@@ -1072,7 +1069,7 @@ impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
     type Map = Map<'tcx>;
 
     /// We want to visit items in the context of their containing
@@ -1087,39 +1084,22 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
     }
 
     fn visit_nested_body(&mut self, body: hir::BodyId) {
-        let orig_tables = mem::replace(&mut self.tables, self.tcx.body_tables(body));
+        let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body));
         let body = self.tcx.hir().body(body);
         self.visit_body(body);
-        self.tables = orig_tables;
+        self.maybe_typeck_tables = old_maybe_typeck_tables;
     }
 
     fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
-        let orig_current_item = mem::replace(&mut self.current_item, Some(item.hir_id));
-        let orig_tables =
-            mem::replace(&mut self.tables, item_tables(self.tcx, item.hir_id, self.empty_tables));
+        let orig_current_item = self.current_item.replace(item.hir_id);
         intravisit::walk_item(self, item);
         self.current_item = orig_current_item;
-        self.tables = orig_tables;
-    }
-
-    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
-        let orig_tables =
-            mem::replace(&mut self.tables, item_tables(self.tcx, ti.hir_id, self.empty_tables));
-        intravisit::walk_trait_item(self, ti);
-        self.tables = orig_tables;
-    }
-
-    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
-        let orig_tables =
-            mem::replace(&mut self.tables, item_tables(self.tcx, ii.hir_id, self.empty_tables));
-        intravisit::walk_impl_item(self, ii);
-        self.tables = orig_tables;
     }
 
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
         if let hir::ExprKind::Struct(ref qpath, fields, ref base) = expr.kind {
-            let res = self.tables.qpath_res(qpath, expr.hir_id);
-            let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap();
+            let res = self.tables().qpath_res(qpath, expr.hir_id);
+            let adt = self.tables().expr_ty(expr).ty_adt_def().unwrap();
             let variant = adt.variant_of_res(res);
             if let Some(ref base) = *base {
                 // If the expression uses FRU we need to make sure all the unmentioned fields
@@ -1128,7 +1108,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
                 for (vf_index, variant_field) in variant.fields.iter().enumerate() {
                     let field = fields
                         .iter()
-                        .find(|f| self.tcx.field_index(f.hir_id, self.tables) == vf_index);
+                        .find(|f| self.tcx.field_index(f.hir_id, self.tables()) == vf_index);
                     let (use_ctxt, span) = match field {
                         Some(field) => (field.ident.span, field.span),
                         None => (base.span, base.span),
@@ -1138,7 +1118,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
             } else {
                 for field in fields {
                     let use_ctxt = field.ident.span;
-                    let index = self.tcx.field_index(field.hir_id, self.tables);
+                    let index = self.tcx.field_index(field.hir_id, self.tables());
                     self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false);
                 }
             }
@@ -1149,12 +1129,12 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
 
     fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
         if let PatKind::Struct(ref qpath, fields, _) = pat.kind {
-            let res = self.tables.qpath_res(qpath, pat.hir_id);
-            let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap();
+            let res = self.tables().qpath_res(qpath, pat.hir_id);
+            let adt = self.tables().pat_ty(pat).ty_adt_def().unwrap();
             let variant = adt.variant_of_res(res);
             for field in fields {
                 let use_ctxt = field.ident.span;
-                let index = self.tcx.field_index(field.hir_id, self.tables);
+                let index = self.tcx.field_index(field.hir_id, self.tables());
                 self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false);
             }
         }
@@ -1169,16 +1149,22 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
 /// Checks are performed on "semantic" types regardless of names and their hygiene.
 ////////////////////////////////////////////////////////////////////////////////////////////
 
-struct TypePrivacyVisitor<'a, 'tcx> {
+struct TypePrivacyVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
-    tables: &'a ty::TypeckTables<'tcx>,
+    maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>,
     current_item: LocalDefId,
-    in_body: bool,
     span: Span,
-    empty_tables: &'a ty::TypeckTables<'tcx>,
 }
 
-impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
+impl<'tcx> TypePrivacyVisitor<'tcx> {
+    /// Gets the type-checking side-tables for the current body.
+    /// As this will ICE if called outside bodies, only call when working with
+    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
+    #[track_caller]
+    fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
+        self.maybe_typeck_tables.expect("`TypePrivacyVisitor::tables` called outside of body")
+    }
+
     fn item_is_accessible(&self, did: DefId) -> bool {
         def_id_visibility(self.tcx, did)
             .0
@@ -1188,10 +1174,11 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
     // Take node-id of an expression or pattern and check its type for privacy.
     fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool {
         self.span = span;
-        if self.visit(self.tables.node_type(id)) || self.visit(self.tables.node_substs(id)) {
+        let tables = self.tables();
+        if self.visit(tables.node_type(id)) || self.visit(tables.node_substs(id)) {
             return true;
         }
-        if let Some(adjustments) = self.tables.adjustments().get(id) {
+        if let Some(adjustments) = tables.adjustments().get(id) {
             for adjustment in adjustments {
                 if self.visit(adjustment.target) {
                     return true;
@@ -1214,7 +1201,7 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
     type Map = Map<'tcx>;
 
     /// We want to visit items in the context of their containing
@@ -1229,19 +1216,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
     }
 
     fn visit_nested_body(&mut self, body: hir::BodyId) {
-        let orig_tables = mem::replace(&mut self.tables, self.tcx.body_tables(body));
-        let orig_in_body = mem::replace(&mut self.in_body, true);
+        let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body));
         let body = self.tcx.hir().body(body);
         self.visit_body(body);
-        self.tables = orig_tables;
-        self.in_body = orig_in_body;
+        self.maybe_typeck_tables = old_maybe_typeck_tables;
     }
 
     fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
         self.span = hir_ty.span;
-        if self.in_body {
+        if let Some(tables) = self.maybe_typeck_tables {
             // Types in bodies.
-            if self.visit(self.tables.node_type(hir_ty.hir_id)) {
+            if self.visit(tables.node_type(hir_ty.hir_id)) {
                 return;
             }
         } else {
@@ -1258,7 +1243,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
 
     fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef<'tcx>) {
         self.span = trait_ref.path.span;
-        if !self.in_body {
+        if self.maybe_typeck_tables.is_none() {
             // Avoid calling `hir_trait_to_predicates` in bodies, it will ICE.
             // The traits' privacy in bodies is already checked as a part of trait object types.
             let bounds = rustc_typeck::hir_trait_to_predicates(
@@ -1304,7 +1289,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
             hir::ExprKind::MethodCall(_, span, _, _) => {
                 // Method calls have to be checked specially.
                 self.span = span;
-                if let Some(def_id) = self.tables.type_dependent_def_id(expr.hir_id) {
+                if let Some(def_id) = self.tables().type_dependent_def_id(expr.hir_id) {
                     if self.visit(self.tcx.type_of(def_id)) {
                         return;
                     }
@@ -1327,9 +1312,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
     // more code internal visibility at link time. (Access to private functions
     // is already prohibited by type privacy for function types.)
     fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: hir::HirId, span: Span) {
-        let def = match self.tables.qpath_res(qpath, id) {
-            Res::Def(kind, def_id) => Some((kind, def_id)),
-            _ => None,
+        let def = match qpath {
+            hir::QPath::Resolved(_, path) => match path.res {
+                Res::Def(kind, def_id) => Some((kind, def_id)),
+                _ => None,
+            },
+            hir::QPath::TypeRelative(..) => {
+                self.maybe_typeck_tables.and_then(|tables| tables.type_dependent_def(id))
+            }
         };
         let def = def.filter(|(kind, _)| match kind {
             DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static => true,
@@ -1385,31 +1375,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
     fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
         let orig_current_item =
             mem::replace(&mut self.current_item, self.tcx.hir().local_def_id(item.hir_id));
-        let orig_in_body = mem::replace(&mut self.in_body, false);
-        let orig_tables =
-            mem::replace(&mut self.tables, item_tables(self.tcx, item.hir_id, self.empty_tables));
+        let old_maybe_typeck_tables = self.maybe_typeck_tables.take();
         intravisit::walk_item(self, item);
-        self.tables = orig_tables;
-        self.in_body = orig_in_body;
+        self.maybe_typeck_tables = old_maybe_typeck_tables;
         self.current_item = orig_current_item;
     }
-
-    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
-        let orig_tables =
-            mem::replace(&mut self.tables, item_tables(self.tcx, ti.hir_id, self.empty_tables));
-        intravisit::walk_trait_item(self, ti);
-        self.tables = orig_tables;
-    }
-
-    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
-        let orig_tables =
-            mem::replace(&mut self.tables, item_tables(self.tcx, ii.hir_id, self.empty_tables));
-        intravisit::walk_impl_item(self, ii);
-        self.tables = orig_tables;
-    }
 }
 
-impl DefIdVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
+impl DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -2066,29 +2039,16 @@ pub fn provide(providers: &mut Providers<'_>) {
 }
 
 fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
-    let empty_tables = ty::TypeckTables::empty(None);
-
     // Check privacy of names not checked in previous compilation stages.
-    let mut visitor = NamePrivacyVisitor {
-        tcx,
-        tables: &empty_tables,
-        current_item: None,
-        empty_tables: &empty_tables,
-    };
+    let mut visitor = NamePrivacyVisitor { tcx, maybe_typeck_tables: None, current_item: None };
     let (module, span, hir_id) = tcx.hir().get_module(module_def_id);
 
     intravisit::walk_mod(&mut visitor, module, hir_id);
 
     // Check privacy of explicitly written types and traits as well as
     // inferred types of expressions and patterns.
-    let mut visitor = TypePrivacyVisitor {
-        tcx,
-        tables: &empty_tables,
-        current_item: module_def_id,
-        in_body: false,
-        span,
-        empty_tables: &empty_tables,
-    };
+    let mut visitor =
+        TypePrivacyVisitor { tcx, maybe_typeck_tables: None, current_item: module_def_id, span };
     intravisit::walk_mod(&mut visitor, module, hir_id);
 }
 
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index e63e31e03c9..38be97fc8d9 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -109,15 +109,15 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
         F: FnOnce(&mut Self),
     {
         let tables = if self.tcx.has_typeck_tables(item_def_id) {
-            self.tcx.typeck_tables_of(item_def_id)
+            Some(self.tcx.typeck_tables_of(item_def_id))
         } else {
-            self.save_ctxt.empty_tables
+            None
         };
 
-        let old_tables = self.save_ctxt.tables;
-        self.save_ctxt.tables = tables;
+        let old_maybe_typeck_tables = self.save_ctxt.maybe_typeck_tables;
+        self.save_ctxt.maybe_typeck_tables = tables;
         f(self);
-        self.save_ctxt.tables = old_tables;
+        self.save_ctxt.maybe_typeck_tables = old_maybe_typeck_tables;
     }
 
     fn span_from_span(&self, span: Span) -> SpanData {
@@ -226,7 +226,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
             collector.visit_pat(&arg.pat);
 
             for (hir_id, ident, ..) in collector.collected_idents {
-                let typ = match self.save_ctxt.tables.node_type_opt(hir_id) {
+                let typ = match self.save_ctxt.tables().node_type_opt(hir_id) {
                     Some(s) => s.to_string(),
                     None => continue,
                 };
@@ -859,7 +859,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
         match p.kind {
             hir::PatKind::Struct(ref _path, fields, _) => {
                 // FIXME do something with _path?
-                let adt = match self.save_ctxt.tables.node_type_opt(p.hir_id) {
+                let adt = match self.save_ctxt.tables().node_type_opt(p.hir_id) {
                     Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(),
                     _ => {
                         intravisit::walk_pat(self, p);
@@ -900,7 +900,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
                 Res::Local(hir_id) => {
                     let typ = self
                         .save_ctxt
-                        .tables
+                        .tables()
                         .node_type_opt(hir_id)
                         .map(|t| t.to_string())
                         .unwrap_or_default();
@@ -1393,7 +1393,7 @@ impl<'l, 'tcx> Visitor<'tcx> for DumpVisitor<'l, 'tcx> {
         match ex.kind {
             hir::ExprKind::Struct(ref path, ref fields, ref base) => {
                 let hir_expr = self.save_ctxt.tcx.hir().expect_expr(ex.hir_id);
-                let adt = match self.save_ctxt.tables.expr_ty_opt(&hir_expr) {
+                let adt = match self.save_ctxt.tables().expr_ty_opt(&hir_expr) {
                     Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(),
                     _ => {
                         intravisit::walk_expr(self, ex);
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index f5c3e84c624..1ac91289a86 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -1,6 +1,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
 #![feature(nll)]
 #![feature(or_patterns)]
+#![cfg_attr(bootstrap, feature(track_caller))]
 #![recursion_limit = "256"]
 
 mod dump_visitor;
@@ -47,12 +48,9 @@ use rls_data::{
 
 use log::{debug, error, info};
 
-pub struct SaveContext<'l, 'tcx> {
+pub struct SaveContext<'l, 'tcx: 'l> {
     tcx: TyCtxt<'tcx>,
-    tables: &'l ty::TypeckTables<'tcx>,
-    /// Used as a fallback when nesting the typeck tables during item processing
-    /// (if these are not available for that item, e.g. don't own a body)
-    empty_tables: &'l ty::TypeckTables<'tcx>,
+    maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>,
     access_levels: &'l AccessLevels,
     span_utils: SpanUtils<'tcx>,
     config: Config,
@@ -67,6 +65,14 @@ pub enum Data {
 }
 
 impl<'l, 'tcx> SaveContext<'l, 'tcx> {
+    /// Gets the type-checking side-tables for the current body.
+    /// As this will ICE if called outside bodies, only call when working with
+    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
+    #[track_caller]
+    fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
+        self.maybe_typeck_tables.expect("`SaveContext::tables` called outside of body")
+    }
+
     fn span_from_span(&self, span: Span) -> SpanData {
         use rls_span::{Column, Row};
 
@@ -518,13 +524,13 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
     }
 
     pub fn get_expr_data(&self, expr: &hir::Expr<'_>) -> Option<Data> {
-        let ty = self.tables.expr_ty_adjusted_opt(expr)?;
+        let ty = self.tables().expr_ty_adjusted_opt(expr)?;
         if matches!(ty.kind, ty::Error(_)) {
             return None;
         }
         match expr.kind {
             hir::ExprKind::Field(ref sub_ex, ident) => {
-                match self.tables.expr_ty_adjusted(&sub_ex).kind {
+                match self.tables().expr_ty_adjusted(&sub_ex).kind {
                     ty::Adt(def, _) if !def.is_enum() => {
                         let variant = &def.non_enum_variant();
                         filter!(self.span_utils, ident.span);
@@ -569,7 +575,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
                 }
             }
             hir::ExprKind::MethodCall(ref seg, ..) => {
-                let method_id = match self.tables.type_dependent_def_id(expr.hir_id) {
+                let method_id = match self.tables().type_dependent_def_id(expr.hir_id) {
                     Some(id) => id,
                     None => {
                         debug!("could not resolve method id for {:?}", expr);
@@ -618,7 +624,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
             },
 
             Node::Expr(&hir::Expr { kind: hir::ExprKind::Struct(ref qpath, ..), .. }) => {
-                self.tables.qpath_res(qpath, hir_id)
+                self.tables().qpath_res(qpath, hir_id)
             }
 
             Node::Expr(&hir::Expr { kind: hir::ExprKind::Path(ref qpath), .. })
@@ -629,9 +635,12 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
                     | hir::PatKind::TupleStruct(ref qpath, ..),
                 ..
             })
-            | Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => {
-                self.tables.qpath_res(qpath, hir_id)
-            }
+            | Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => match qpath {
+                hir::QPath::Resolved(_, path) => path.res,
+                hir::QPath::TypeRelative(..) => self
+                    .maybe_typeck_tables
+                    .map_or(Res::Err, |tables| tables.qpath_res(qpath, hir_id)),
+            },
 
             Node::Binding(&hir::Pat {
                 kind: hir::PatKind::Binding(_, canonical_id, ..), ..
@@ -1001,8 +1010,7 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>(
 
         let save_ctxt = SaveContext {
             tcx,
-            tables: &ty::TypeckTables::empty(None),
-            empty_tables: &ty::TypeckTables::empty(None),
+            maybe_typeck_tables: None,
             access_levels: &access_levels,
             span_utils: SpanUtils::new(&tcx.sess),
             config: find_config(config),
diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
index 1fafa9ec035..cdfe5f9f92d 100644
--- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
@@ -1407,7 +1407,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         );
         let query_tables;
         let tables: &TypeckTables<'tcx> = match &in_progress_tables {
-            Some(t) if t.hir_owner.map(|owner| owner.to_def_id()) == Some(generator_did_root) => t,
+            Some(t) if t.hir_owner.to_def_id() == generator_did_root => t,
             _ => {
                 query_tables = self.tcx.typeck_tables_of(generator_did.expect_local());
                 &query_tables
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index b75dac52b93..fbf81e94d03 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -1039,22 +1039,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let mut suggested = false;
             if let (Some(ref param), Some(ref table)) = (param_type, self.in_progress_tables) {
                 let table_owner = table.borrow().hir_owner;
-                if let Some(table_owner) = table_owner {
-                    let generics = self.tcx.generics_of(table_owner.to_def_id());
-                    let type_param = generics.type_param(param, self.tcx);
-                    let hir = &self.tcx.hir();
-                    if let Some(def_id) = type_param.def_id.as_local() {
-                        let id = hir.as_local_hir_id(def_id);
-                        // Get the `hir::Param` to verify whether it already has any bounds.
-                        // We do this to avoid suggesting code that ends up as `T: FooBar`,
-                        // instead we suggest `T: Foo + Bar` in that case.
-                        match hir.get(id) {
-                            Node::GenericParam(ref param) => {
-                                let mut impl_trait = false;
-                                let has_bounds = if let hir::GenericParamKind::Type {
-                                    synthetic: Some(_),
-                                    ..
-                                } = &param.kind
+                let generics = self.tcx.generics_of(table_owner.to_def_id());
+                let type_param = generics.type_param(param, self.tcx);
+                let hir = &self.tcx.hir();
+                if let Some(def_id) = type_param.def_id.as_local() {
+                    let id = hir.as_local_hir_id(def_id);
+                    // Get the `hir::Param` to verify whether it already has any bounds.
+                    // We do this to avoid suggesting code that ends up as `T: FooBar`,
+                    // instead we suggest `T: Foo + Bar` in that case.
+                    match hir.get(id) {
+                        Node::GenericParam(ref param) => {
+                            let mut impl_trait = false;
+                            let has_bounds =
+                                if let hir::GenericParamKind::Type { synthetic: Some(_), .. } =
+                                    &param.kind
                                 {
                                     // We've found `fn foo(x: impl Trait)` instead of
                                     // `fn foo<T>(x: T)`. We want to suggest the correct
@@ -1065,64 +1063,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 } else {
                                     param.bounds.get(0)
                                 };
-                                let sp = hir.span(id);
-                                let sp = if let Some(first_bound) = has_bounds {
-                                    // `sp` only covers `T`, change it so that it covers
-                                    // `T:` when appropriate
-                                    sp.until(first_bound.span())
-                                } else {
-                                    sp
-                                };
-                                let trait_def_ids: FxHashSet<DefId> = param
-                                    .bounds
-                                    .iter()
-                                    .filter_map(|bound| Some(bound.trait_ref()?.trait_def_id()?))
-                                    .collect();
-                                if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) {
-                                    err.span_suggestions(
-                                        sp,
-                                        &message(format!(
-                                            "restrict type parameter `{}` with",
-                                            param.name.ident(),
-                                        )),
-                                        candidates.iter().map(|t| {
-                                            format!(
-                                                "{}{} {}{}",
-                                                param.name.ident(),
-                                                if impl_trait { " +" } else { ":" },
-                                                self.tcx.def_path_str(t.def_id),
-                                                if has_bounds.is_some() { " + " } else { "" },
-                                            )
-                                        }),
-                                        Applicability::MaybeIncorrect,
-                                    );
-                                }
-                                suggested = true;
-                            }
-                            Node::Item(hir::Item {
-                                kind: hir::ItemKind::Trait(.., bounds, _),
-                                ident,
-                                ..
-                            }) => {
-                                let (sp, sep, article) = if bounds.is_empty() {
-                                    (ident.span.shrink_to_hi(), ":", "a")
-                                } else {
-                                    (bounds.last().unwrap().span().shrink_to_hi(), " +", "another")
-                                };
+                            let sp = hir.span(id);
+                            let sp = if let Some(first_bound) = has_bounds {
+                                // `sp` only covers `T`, change it so that it covers
+                                // `T:` when appropriate
+                                sp.until(first_bound.span())
+                            } else {
+                                sp
+                            };
+                            let trait_def_ids: FxHashSet<DefId> = param
+                                .bounds
+                                .iter()
+                                .filter_map(|bound| Some(bound.trait_ref()?.trait_def_id()?))
+                                .collect();
+                            if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) {
                                 err.span_suggestions(
                                     sp,
-                                    &message(format!("add {} supertrait for", article)),
+                                    &message(format!(
+                                        "restrict type parameter `{}` with",
+                                        param.name.ident(),
+                                    )),
                                     candidates.iter().map(|t| {
-                                        format!("{} {}", sep, self.tcx.def_path_str(t.def_id),)
+                                        format!(
+                                            "{}{} {}{}",
+                                            param.name.ident(),
+                                            if impl_trait { " +" } else { ":" },
+                                            self.tcx.def_path_str(t.def_id),
+                                            if has_bounds.is_some() { " + " } else { "" },
+                                        )
                                     }),
                                     Applicability::MaybeIncorrect,
                                 );
-                                suggested = true;
                             }
-                            _ => {}
+                            suggested = true;
+                        }
+                        Node::Item(hir::Item {
+                            kind: hir::ItemKind::Trait(.., bounds, _),
+                            ident,
+                            ..
+                        }) => {
+                            let (sp, sep, article) = if bounds.is_empty() {
+                                (ident.span.shrink_to_hi(), ":", "a")
+                            } else {
+                                (bounds.last().unwrap().span().shrink_to_hi(), " +", "another")
+                            };
+                            err.span_suggestions(
+                                sp,
+                                &message(format!("add {} supertrait for", article)),
+                                candidates.iter().map(|t| {
+                                    format!("{} {}", sep, self.tcx.def_path_str(t.def_id),)
+                                }),
+                                Applicability::MaybeIncorrect,
+                            );
+                            suggested = true;
                         }
+                        _ => {}
                     }
-                };
+                }
             }
 
             if !suggested {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index b617937d6bd..58fd0f989c6 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1126,7 +1126,7 @@ fn typeck_tables_of_with_fallback<'tcx>(
 
     // Consistency check our TypeckTables instance can hold all ItemLocalIds
     // it will need to hold.
-    assert_eq!(tables.hir_owner, Some(id.owner));
+    assert_eq!(tables.hir_owner, id.owner);
 
     tables
 }
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 4704d8fc766..fa17696e02b 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -109,12 +109,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     ) -> WritebackCx<'cx, 'tcx> {
         let owner = body.id().hir_id.owner;
 
-        WritebackCx {
-            fcx,
-            tables: ty::TypeckTables::empty(Some(owner)),
-            body,
-            rustc_dump_user_substs,
-        }
+        WritebackCx { fcx, tables: ty::TypeckTables::new(owner), body, rustc_dump_user_substs }
     }
 
     fn tcx(&self) -> TyCtxt<'tcx> {
@@ -342,7 +337,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     fn visit_closures(&mut self) {
         let fcx_tables = self.fcx.tables.borrow();
         assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner);
-        let common_hir_owner = fcx_tables.hir_owner.unwrap();
+        let common_hir_owner = fcx_tables.hir_owner;
 
         for (&id, &origin) in fcx_tables.closure_kind_origins().iter() {
             let hir_id = hir::HirId { owner: common_hir_owner, local_id: id };
@@ -363,7 +358,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     fn visit_user_provided_tys(&mut self) {
         let fcx_tables = self.fcx.tables.borrow();
         assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner);
-        let common_hir_owner = fcx_tables.hir_owner.unwrap();
+        let common_hir_owner = fcx_tables.hir_owner;
 
         let mut errors_buffer = Vec::new();
         for (&local_id, c_ty) in fcx_tables.user_provided_types().iter() {
@@ -561,7 +556,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     fn visit_liberated_fn_sigs(&mut self) {
         let fcx_tables = self.fcx.tables.borrow();
         assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner);
-        let common_hir_owner = fcx_tables.hir_owner.unwrap();
+        let common_hir_owner = fcx_tables.hir_owner;
 
         for (&local_id, fn_sig) in fcx_tables.liberated_fn_sigs().iter() {
             let hir_id = hir::HirId { owner: common_hir_owner, local_id };
@@ -573,7 +568,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     fn visit_fru_field_types(&mut self) {
         let fcx_tables = self.fcx.tables.borrow();
         assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner);
-        let common_hir_owner = fcx_tables.hir_owner.unwrap();
+        let common_hir_owner = fcx_tables.hir_owner;
 
         for (&local_id, ftys) in fcx_tables.fru_field_types().iter() {
             let hir_id = hir::HirId { owner: common_hir_owner, local_id };
diff --git a/src/tools/clippy/clippy_lints/src/atomic_ordering.rs b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs
index 65e1e2c519c..1115ba1bf92 100644
--- a/src/tools/clippy/clippy_lints/src/atomic_ordering.rs
+++ b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs
@@ -76,7 +76,7 @@ fn check_atomic_load_store(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
         if method == "load" || method == "store";
         let ordering_arg = if method == "load" { &args[1] } else { &args[2] };
         if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind;
-        if let Some(ordering_def_id) = cx.tables().qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
+        if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
         then {
             if method == "load" &&
                 match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) {
@@ -107,12 +107,12 @@ fn check_memory_fence(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
     if_chain! {
         if let ExprKind::Call(ref func, ref args) = expr.kind;
         if let ExprKind::Path(ref func_qpath) = func.kind;
-        if let Some(def_id) = cx.tables().qpath_res(func_qpath, func.hir_id).opt_def_id();
+        if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
         if ["fence", "compiler_fence"]
             .iter()
             .any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func]));
         if let ExprKind::Path(ref ordering_qpath) = &args[0].kind;
-        if let Some(ordering_def_id) = cx.tables().qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
+        if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
         if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]);
         then {
             span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index efd4a31f559..8cc69c806aa 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -192,7 +192,7 @@ fn lint_same_then_else(cx: &LateContext<'_, '_>, blocks: &[&Block<'_>]) {
 /// Implementation of `IFS_SAME_COND`.
 fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) {
     let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
-        let mut h = SpanlessHash::new(cx, cx.tables());
+        let mut h = SpanlessHash::new(cx);
         h.hash_expr(expr);
         h.finish()
     };
@@ -215,7 +215,7 @@ fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) {
 /// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
 fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) {
     let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
-        let mut h = SpanlessHash::new(cx, cx.tables());
+        let mut h = SpanlessHash::new(cx);
         h.hash_expr(expr);
         h.finish()
     };
@@ -251,7 +251,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) {
 
     if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
         let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
-            let mut h = SpanlessHash::new(cx, cx.tables());
+            let mut h = SpanlessHash::new(cx);
             h.hash_expr(&arm.body);
             h.finish()
         };
diff --git a/src/tools/clippy/clippy_lints/src/default_trait_access.rs b/src/tools/clippy/clippy_lints/src/default_trait_access.rs
index 69ae509fb23..a918c72fb27 100644
--- a/src/tools/clippy/clippy_lints/src/default_trait_access.rs
+++ b/src/tools/clippy/clippy_lints/src/default_trait_access.rs
@@ -36,7 +36,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DefaultTraitAccess {
             if let ExprKind::Call(ref path, ..) = expr.kind;
             if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
             if let ExprKind::Path(ref qpath) = path.kind;
-            if let Some(def_id) = cx.tables().qpath_res(qpath, path.hir_id).opt_def_id();
+            if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
             if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
             then {
                 match qpath {
diff --git a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
index 04af6c2c1f8..31fe7382e46 100644
--- a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
+++ b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
@@ -75,7 +75,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EvalOrderDependence {
                 if let ExprKind::Path(ref qpath) = lhs.kind {
                     if let QPath::Resolved(_, ref path) = *qpath {
                         if path.segments.len() == 1 {
-                            if let def::Res::Local(var) = cx.tables().qpath_res(qpath, lhs.hir_id) {
+                            if let def::Res::Local(var) = cx.qpath_res(qpath, lhs.hir_id) {
                                 let mut visitor = ReadVisitor {
                                     cx,
                                     var,
@@ -309,7 +309,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
                 if_chain! {
                     if let QPath::Resolved(None, ref path) = *qpath;
                     if path.segments.len() == 1;
-                    if let def::Res::Local(local_id) = self.cx.tables().qpath_res(qpath, expr.hir_id);
+                    if let def::Res::Local(local_id) = self.cx.qpath_res(qpath, expr.hir_id);
                     if local_id == self.var;
                     // Check that this is a read, not a write.
                     if !is_in_assignment_position(self.cx, expr);
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index 58cf0027ea4..dc8795e3d11 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -88,7 +88,7 @@ fn on_argumentv1_new<'a, 'tcx>(
         // matches `core::fmt::Display::fmt`
         if args.len() == 2;
         if let ExprKind::Path(ref qpath) = args[1].kind;
-        if let Some(did) = cx.tables().qpath_res(qpath, args[1].hir_id).opt_def_id();
+        if let Some(did) = cx.qpath_res(qpath, args[1].hir_id).opt_def_id();
         if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD);
         // check `(arg0,)` in match block
         if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind;
diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs
index 5a0531ff749..b43babc1de8 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_return.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs
@@ -108,7 +108,7 @@ fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
         ExprKind::Call(expr, ..) => {
             if_chain! {
                 if let ExprKind::Path(qpath) = &expr.kind;
-                if let Some(path_def_id) = cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id();
+                if let Some(path_def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id();
                 if match_def_path(cx, path_def_id, &BEGIN_PANIC) ||
                     match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT);
                 then { }
diff --git a/src/tools/clippy/clippy_lints/src/let_and_return.rs b/src/tools/clippy/clippy_lints/src/let_and_return.rs
index 299202981b1..7d338cfa86f 100644
--- a/src/tools/clippy/clippy_lints/src/let_and_return.rs
+++ b/src/tools/clippy/clippy_lints/src/let_and_return.rs
@@ -107,7 +107,7 @@ impl BorrowVisitor<'_, '_> {
                     ..
                 },
                 ..,
-            ) => self.cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(),
+            ) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(),
             _ => None,
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 6840e82d4bf..c0d8c1127b9 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -343,7 +343,7 @@ impl<'v, 't> RefVisitor<'v, 't> {
                 })
             {
                 let hir_id = ty.hir_id;
-                match self.cx.tables().qpath_res(qpath, hir_id) {
+                match self.cx.qpath_res(qpath, hir_id) {
                     Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => {
                         let generics = self.cx.tcx.generics_of(def_id);
                         for _ in generics.params.as_slice() {
diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
index a4550f707ee..90d4a34a19a 100644
--- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
@@ -160,10 +160,10 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a hir::Expr<'_>)
     }
 }
 
-fn unit_closure<'a, 'tcx>(
-    cx: &LateContext<'a, 'tcx>,
-    expr: &'a hir::Expr<'a>,
-) -> Option<(&'tcx hir::Param<'tcx>, &'a hir::Expr<'a>)> {
+fn unit_closure<'tcx>(
+    cx: &LateContext<'_, 'tcx>,
+    expr: &hir::Expr<'_>,
+) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> {
     if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind {
         let body = cx.tcx.hir().body(inner_expr_id);
         let body_expr = &body.value;
diff --git a/src/tools/clippy/clippy_lints/src/mem_discriminant.rs b/src/tools/clippy/clippy_lints/src/mem_discriminant.rs
index d315c5ef89a..8a665a6e1fa 100644
--- a/src/tools/clippy/clippy_lints/src/mem_discriminant.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_discriminant.rs
@@ -35,7 +35,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant {
             if let ExprKind::Call(ref func, ref func_args) = expr.kind;
             // is `mem::discriminant`
             if let ExprKind::Path(ref func_qpath) = func.kind;
-            if let Some(def_id) = cx.tables().qpath_res(func_qpath, func.hir_id).opt_def_id();
+            if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
             if match_def_path(cx, def_id, &paths::MEM_DISCRIMINANT);
             // type is non-enum
             let ty_param = cx.tables().node_substs(func.hir_id).type_at(0);
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 16d31fc8346..703f91f8ac0 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -162,7 +162,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Ex
         if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind;
         if repl_args.is_empty();
         if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
-        if let Some(repl_def_id) = cx.tables().qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
+        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
         then {
             if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
                 let mut applicability = Applicability::MachineApplicable;
@@ -198,7 +198,7 @@ fn check_replace_with_default(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &E
         if_chain! {
             if !in_external_macro(cx.tcx.sess, expr_span);
             if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
-            if let Some(repl_def_id) = cx.tables().qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
+            if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
             if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD);
             then {
                 span_lint_and_then(
@@ -230,7 +230,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
             // Check that `expr` is a call to `mem::replace()`
             if let ExprKind::Call(ref func, ref func_args) = expr.kind;
             if let ExprKind::Path(ref func_qpath) = func.kind;
-            if let Some(def_id) = cx.tables().qpath_res(func_qpath, func.hir_id).opt_def_id();
+            if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
             if match_def_path(cx, def_id, &paths::MEM_REPLACE);
             if let [dest, src] = &**func_args;
             then {
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index c4e707ecf03..7018a2f4039 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -1824,7 +1824,7 @@ fn lint_expect_fun_call(
             hir::ExprKind::Lit(_) => true,
             hir::ExprKind::Call(fun, _) => {
                 if let hir::ExprKind::Path(ref p) = fun.kind {
-                    match cx.tables().qpath_res(p, fun.hir_id) {
+                    match cx.qpath_res(p, fun.hir_id) {
                         hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
                             cx.tcx.fn_sig(def_id).output().skip_binder().kind,
                             ty::Ref(ty::ReStatic, ..)
@@ -1844,7 +1844,7 @@ fn lint_expect_fun_call(
                         ty::Ref(ty::ReStatic, ..)
                     )
                 }),
-            hir::ExprKind::Path(ref p) => match cx.tables().qpath_res(p, arg.hir_id) {
+            hir::ExprKind::Path(ref p) => match cx.qpath_res(p, arg.hir_id) {
                 hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true,
                 _ => false,
             },
@@ -3317,7 +3317,7 @@ fn lint_option_as_ref_deref<'a, 'tcx>(
                     if_chain! {
                         if args.len() == 1;
                         if let hir::ExprKind::Path(qpath) = &args[0].kind;
-                        if let hir::def::Res::Local(local_id) = cx.tables().qpath_res(qpath, args[0].hir_id);
+                        if let hir::def::Res::Local(local_id) = cx.qpath_res(qpath, args[0].hir_id);
                         if closure_body.params[0].pat.hir_id == local_id;
                         let adj = cx.tables().expr_adjustments(&args[0]).iter().map(|x| &x.kind).collect::<Box<[_]>>();
                         if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj;
@@ -3334,7 +3334,7 @@ fn lint_option_as_ref_deref<'a, 'tcx>(
                         if let hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner1) = inner.kind;
                         if let hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner2) = inner1.kind;
                         if let hir::ExprKind::Path(ref qpath) = inner2.kind;
-                        if let hir::def::Res::Local(local_id) = cx.tables().qpath_res(qpath, inner2.hir_id);
+                        if let hir::def::Res::Local(local_id) = cx.qpath_res(qpath, inner2.hir_id);
                         then {
                             closure_body.params[0].pat.hir_id == local_id
                         } else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
index 88243a88d9d..8a3df85c91b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -65,7 +65,7 @@ fn check_expression<'a, 'tcx>(
                     if match_qpath(path, &paths::OPTION_SOME) {
                         if_chain! {
                             if let hir::ExprKind::Path(path) = &args[0].kind;
-                            if let Res::Local(ref local) = cx.tables().qpath_res(path, args[0].hir_id);
+                            if let Res::Local(ref local) = cx.qpath_res(path, args[0].hir_id);
                             then {
                                 if arg_id == *local {
                                     return (false, false)
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index 99cd864cae4..fcd77088b88 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -436,7 +436,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints {
                     binding != "_result" && // FIXME: #944
                     is_used(cx, expr) &&
                     // don't lint if the declaration is in a macro
-                    non_macro_local(cx, cx.tables().qpath_res(qpath, expr.hir_id))
+                    non_macro_local(cx, cx.qpath_res(qpath, expr.hir_id))
                 {
                     Some(binding)
                 } else {
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index 4a6395da01c..a77e104bb8f 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -158,7 +158,7 @@ impl QuestionMark {
             ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr),
             ExprKind::Path(ref qp) => {
                 if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) =
-                    cx.tables().qpath_res(qp, expression.hir_id)
+                    cx.qpath_res(qp, expression.hir_id)
                 {
                     return match_def_path(cx, def_id, &paths::OPTION_NONE);
                 }
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index 9c54c3cbac0..0b56ef02a84 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -111,7 +111,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex {
             if let ExprKind::Call(ref fun, ref args) = expr.kind;
             if let ExprKind::Path(ref qpath) = fun.kind;
             if args.len() == 1;
-            if let Some(def_id) = cx.tables().qpath_res(qpath, fun.hir_id).opt_def_id();
+            if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
             then {
                 if match_def_path(cx, def_id, &paths::REGEX_NEW) ||
                    match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) {
diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
index f2bbf19bea9..bc282e4bd98 100644
--- a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
+++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
@@ -26,7 +26,7 @@ fn is_temporary(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
     match &expr.kind {
         ExprKind::Struct(..) | ExprKind::Tup(..) => true,
         ExprKind::Path(qpath) => {
-            if let Res::Def(DefKind::Const, ..) = cx.tables().qpath_res(qpath, expr.hir_id) {
+            if let Res::Def(DefKind::Const, ..) = cx.qpath_res(qpath, expr.hir_id) {
                 true
             } else {
                 false
diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
index 1efba3580fe..4f943eeaeeb 100644
--- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
@@ -56,7 +56,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ToDigitIsSome {
                         if_chain! {
                             if let [char_arg, radix_arg] = &**to_digit_args;
                             if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind;
-                            if let to_digits_call_res = cx.tables().qpath_res(to_digits_path, to_digits_call.hir_id);
+                            if let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id);
                             if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id();
                             if match_def_path(cx, to_digits_def_id, &["core", "char", "methods", "<impl char>", "to_digit"]);
                             then {
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 1b233b8302f..c3e4eb05eb4 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -37,7 +37,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TraitBounds {
             return;
         }
         let hash = |ty| -> u64 {
-            let mut hasher = SpanlessHash::new(cx, cx.tables());
+            let mut hasher = SpanlessHash::new(cx);
             hasher.hash_ty(ty);
             hasher.finish()
         };
diff --git a/src/tools/clippy/clippy_lints/src/transmute.rs b/src/tools/clippy/clippy_lints/src/transmute.rs
index 9b134494947..6ef4b8dcfc1 100644
--- a/src/tools/clippy/clippy_lints/src/transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute.rs
@@ -299,7 +299,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
         if_chain! {
             if let ExprKind::Call(ref path_expr, ref args) = e.kind;
             if let ExprKind::Path(ref qpath) = path_expr.kind;
-            if let Some(def_id) = cx.tables().qpath_res(qpath, path_expr.hir_id).opt_def_id();
+            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
             if match_def_path(cx, def_id, &paths::TRANSMUTE);
             then {
                 let from_ty = cx.tables().expr_ty(&args[0]);
diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs
index ecfb6ee2a7d..74db29e4f1d 100644
--- a/src/tools/clippy/clippy_lints/src/types.rs
+++ b/src/tools/clippy/clippy_lints/src/types.rs
@@ -2490,7 +2490,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
 /// Looks for default-hasher-dependent constructors like `HashMap::new`.
 struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
     cx: &'a LateContext<'a, 'tcx>,
-    body: &'a TypeckTables<'tcx>,
+    maybe_typeck_tables: Option<&'tcx TypeckTables<'tcx>>,
     target: &'b ImplicitHasherType<'tcx>,
     suggestions: BTreeMap<Span, String>,
 }
@@ -2499,7 +2499,7 @@ impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
     fn new(cx: &'a LateContext<'a, 'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self {
         Self {
             cx,
-            body: cx.tables(),
+            maybe_typeck_tables: cx.maybe_typeck_tables(),
             target,
             suggestions: BTreeMap::new(),
         }
@@ -2510,10 +2510,9 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
     type Map = Map<'tcx>;
 
     fn visit_body(&mut self, body: &'tcx Body<'_>) {
-        let prev_body = self.body;
-        self.body = self.cx.tcx.body_tables(body.id());
+        let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.cx.tcx.body_tables(body.id()));
         walk_body(self, body);
-        self.body = prev_body;
+        self.maybe_typeck_tables = old_maybe_typeck_tables;
     }
 
     fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
@@ -2522,7 +2521,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
             if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind;
             if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
             then {
-                if !TyS::same_type(self.target.ty(), self.body.expr_ty(e)) {
+                if !TyS::same_type(self.target.ty(), self.maybe_typeck_tables.unwrap().expr_ty(e)) {
                     return;
                 }
 
diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
index 53e47f09ae5..0efbf68dcd8 100644
--- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs
+++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
@@ -98,7 +98,7 @@ impl LateLintPass<'_, '_> for UnnamedAddress {
         if_chain! {
             if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind;
             if let ExprKind::Path(ref func_qpath) = func.kind;
-            if let Some(def_id) = cx.tables().qpath_res(func_qpath, func.hir_id).opt_def_id();
+            if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
             if match_def_path(cx, def_id, &paths::PTR_EQ) ||
                 match_def_path(cx, def_id, &paths::RC_PTR_EQ) ||
                 match_def_path(cx, def_id, &paths::ARC_PTR_EQ);
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index 5d150ad4f03..ad5ecc0c026 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -121,7 +121,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion {
                 if_chain! {
                     if args.len() == 1;
                     if let ExprKind::Path(ref qpath) = path.kind;
-                    if let Some(def_id) = cx.tables().qpath_res(qpath, path.hir_id).opt_def_id();
+                    if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
                     let a = cx.tables().expr_ty(e);
                     let b = cx.tables().expr_ty(&args[0]);
 
diff --git a/src/tools/clippy/clippy_lints/src/utils/higher.rs b/src/tools/clippy/clippy_lints/src/utils/higher.rs
index 0e78f35a129..9502d85e6ee 100644
--- a/src/tools/clippy/clippy_lints/src/utils/higher.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/higher.rs
@@ -262,7 +262,7 @@ pub fn vec_macro<'e>(cx: &LateContext<'_, '_>, expr: &'e hir::Expr<'_>) -> Optio
         if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
         if let hir::ExprKind::Path(ref qpath) = fun.kind;
         if is_expn_of(fun.span, "vec").is_some();
-        if let Some(fun_def_id) = cx.tables().qpath_res(qpath, fun.hir_id).opt_def_id();
+        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
         then {
             return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
                 // `vec![elem; size]` case
diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
index a74ab18a063..bf1017d76ec 100644
--- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
@@ -22,7 +22,7 @@ use std::hash::Hash;
 pub struct SpanlessEq<'a, 'tcx> {
     /// Context used to evaluate constant expressions.
     cx: &'a LateContext<'a, 'tcx>,
-    tables: &'a TypeckTables<'tcx>,
+    maybe_typeck_tables: Option<&'tcx TypeckTables<'tcx>>,
     /// If is true, never consider as equal expressions containing function
     /// calls.
     ignore_fn: bool,
@@ -32,16 +32,15 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
     pub fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
         Self {
             cx,
-            tables: cx.tables(),
+            maybe_typeck_tables: cx.maybe_typeck_tables(),
             ignore_fn: false,
         }
     }
 
     pub fn ignore_fn(self) -> Self {
         Self {
-            cx: self.cx,
-            tables: self.cx.tables(),
             ignore_fn: true,
+            ..self
         }
     }
 
@@ -72,12 +71,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
             return false;
         }
 
-        if let (Some(l), Some(r)) = (
-            constant_simple(self.cx, self.tables, left),
-            constant_simple(self.cx, self.tables, right),
-        ) {
-            if l == r {
-                return true;
+        if let Some(tables) = self.maybe_typeck_tables {
+            if let (Some(l), Some(r)) = (
+                constant_simple(self.cx, tables, left),
+                constant_simple(self.cx, tables, right),
+            ) {
+                if l == r {
+                    return true;
+                }
             }
         }
 
@@ -271,18 +272,18 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
         match (left, right) {
             (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
             (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => {
-                let full_table = self.tables;
+                let old_maybe_typeck_tables = self.maybe_typeck_tables;
 
                 let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(ll_id.body));
-                self.tables = self.cx.tcx.body_tables(ll_id.body);
+                self.maybe_typeck_tables = Some(self.cx.tcx.body_tables(ll_id.body));
                 let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value);
 
                 let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(rl_id.body));
-                self.tables = self.cx.tcx.body_tables(rl_id.body);
+                self.maybe_typeck_tables = Some(self.cx.tcx.body_tables(rl_id.body));
                 let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value);
 
                 let eq_ty = self.eq_ty(lt, rt);
-                self.tables = full_table;
+                self.maybe_typeck_tables = old_maybe_typeck_tables;
                 eq_ty && ll == rl
             },
             (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
@@ -347,15 +348,15 @@ pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -
 pub struct SpanlessHash<'a, 'tcx> {
     /// Context used to evaluate constant expressions.
     cx: &'a LateContext<'a, 'tcx>,
-    tables: &'a TypeckTables<'tcx>,
+    maybe_typeck_tables: Option<&'tcx TypeckTables<'tcx>>,
     s: StableHasher,
 }
 
 impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
-    pub fn new(cx: &'a LateContext<'a, 'tcx>, tables: &'a TypeckTables<'tcx>) -> Self {
+    pub fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
         Self {
             cx,
-            tables,
+            maybe_typeck_tables: cx.maybe_typeck_tables(),
             s: StableHasher::new(),
         }
     }
@@ -384,7 +385,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 
     #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
     pub fn hash_expr(&mut self, e: &Expr<'_>) {
-        let simple_const = constant_simple(self.cx, self.tables, e);
+        let simple_const = self
+            .maybe_typeck_tables
+            .and_then(|tables| constant_simple(self.cx, tables, e));
 
         // const hashing may result in the same hash as some unrelated node, so add a sort of
         // discriminant depending on which path we're choosing next
@@ -599,7 +602,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 self.hash_name(path.ident.name);
             },
         }
-        // self.cx.tables.qpath_res(p, id).hash(&mut self.s);
+        // self.maybe_typeck_tables.unwrap().qpath_res(p, id).hash(&mut self.s);
     }
 
     pub fn hash_path(&mut self, p: &Path<'_>) {
@@ -728,9 +731,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 
     pub fn hash_body(&mut self, body_id: BodyId) {
         // swap out TypeckTables when hashing a body
-        let old_tables = self.tables;
-        self.tables = self.cx.tcx.body_tables(body_id);
+        let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.cx.tcx.body_tables(body_id));
         self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
-        self.tables = old_tables;
+        self.maybe_typeck_tables = old_maybe_typeck_tables;
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index 38468181d02..ca947e9241f 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -347,7 +347,7 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty<'_>) -> bool {
     ) = ty.kind
     {
         if let TyKind::Path(ref path) = inner.kind {
-            if let Res::Def(DefKind::Struct, def_id) = cx.tables().qpath_res(path, inner.hir_id) {
+            if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
                 return match_def_path(cx, def_id, &paths::LINT);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 69ec4b7ad6d..8be9ba2c3c2 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -898,7 +898,7 @@ pub fn is_copy<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
 pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
     if let ExprKind::Call(ref fun, _) = expr.kind {
         if let ExprKind::Path(ref qp) = fun.kind {
-            let res = cx.tables().qpath_res(qp, fun.hir_id);
+            let res = cx.qpath_res(qp, fun.hir_id);
             return match res {
                 def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
                 def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
@@ -914,7 +914,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Exp
 pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool {
     fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool {
         matches!(
-            cx.tables().qpath_res(qpath, id),
+            cx.qpath_res(qpath, id),
             def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
         )
     }
@@ -1190,7 +1190,7 @@ pub fn match_function_call<'a, 'tcx>(
     if_chain! {
         if let ExprKind::Call(ref fun, ref args) = expr.kind;
         if let ExprKind::Path(ref qpath) = fun.kind;
-        if let Some(fun_def_id) = cx.tables().qpath_res(qpath, fun.hir_id).opt_def_id();
+        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
         if match_def_path(cx, fun_def_id, path);
         then {
             return Some(&args)
@@ -1317,7 +1317,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool
     let did = match expr.kind {
         ExprKind::Call(ref path, _) => if_chain! {
             if let ExprKind::Path(ref qpath) = path.kind;
-            if let def::Res::Def(_, did) = cx.tables().qpath_res(qpath, path.hir_id);
+            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
             then {
                 Some(did)
             } else {