Skip to content

Commit 14e65d5

Browse files
committed
Auto merge of #73743 - eddyb:lint-on-demand-typeck-tables, r=Manishearth
rustc_lint: only query `typeck_tables_of` when a lint needs it. This was prompted by @jyn514 running into a situation where `rustdoc` wants to run the `unused_doc` lint without triggering type-checking (as an alternative to the "everybody loops" approach - type-checking may error/ICE because of the `rustdoc` feature of allowing multi-platform docs where the actual bodies of functions may refer to APIs for different platforms). There was also this comment in the source: ```rust // FIXME: Make this lazy to avoid running the TypeckTables query? ``` The main effect of this is for lint authors, who now need to use `cx.tables()` to get `&TypeckTables`, as opposed to having them always available in `cx.tables`. r? @oli-obk or @Manishearth
2 parents e093b65 + 0cf4b9d commit 14e65d5

File tree

106 files changed

+441
-401
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+441
-401
lines changed

src/librustc_lint/array_into_iter.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter {
3131

3232
// Check if the method call actually calls the libcore
3333
// `IntoIterator::into_iter`.
34-
let def_id = cx.tables.type_dependent_def_id(expr.hir_id).unwrap();
34+
let def_id = cx.tables().type_dependent_def_id(expr.hir_id).unwrap();
3535
match cx.tcx.trait_of_item(def_id) {
3636
Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {}
3737
_ => return,
@@ -45,7 +45,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter {
4545
// `Box` is the only thing that values can be moved out of via
4646
// method call. `Box::new([1]).into_iter()` should trigger this
4747
// lint.
48-
let mut recv_ty = cx.tables.expr_ty(receiver_arg);
48+
let mut recv_ty = cx.tables().expr_ty(receiver_arg);
4949
let mut num_box_derefs = 0;
5050
while recv_ty.is_box() {
5151
num_box_derefs += 1;
@@ -60,13 +60,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter {
6060
// Make sure that there is an autoref coercion at the expected
6161
// position. The first `num_box_derefs` adjustments are the derefs
6262
// of the box.
63-
match cx.tables.expr_adjustments(receiver_arg).get(num_box_derefs) {
63+
match cx.tables().expr_adjustments(receiver_arg).get(num_box_derefs) {
6464
Some(Adjustment { kind: Adjust::Borrow(_), .. }) => {}
6565
_ => return,
6666
}
6767

6868
// Emit lint diagnostic.
69-
let target = match cx.tables.expr_ty_adjusted(receiver_arg).kind {
69+
let target = match cx.tables().expr_ty_adjusted(receiver_arg).kind {
7070
ty::Ref(_, ty::TyS { kind: ty::Array(..), .. }, _) => "[T; N]",
7171
ty::Ref(_, ty::TyS { kind: ty::Slice(..), .. }, _) => "[T]",
7272

src/librustc_lint/builtin.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxPointers {
144144
}
145145

146146
fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) {
147-
let ty = cx.tables.node_type(e.hir_id);
147+
let ty = cx.tables().node_type(e.hir_id);
148148
self.check_heap_type(cx, e.span, ty);
149149
}
150150
}
@@ -161,11 +161,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
161161
fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) {
162162
if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind {
163163
let variant = cx
164-
.tables
164+
.tables()
165165
.pat_ty(pat)
166166
.ty_adt_def()
167167
.expect("struct pattern type is not an ADT")
168-
.variant_of_res(cx.tables.qpath_res(qpath, pat.hir_id));
168+
.variant_of_res(cx.tables().qpath_res(qpath, pat.hir_id));
169169
for fieldpat in field_pats {
170170
if fieldpat.is_shorthand {
171171
continue;
@@ -178,7 +178,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
178178
}
179179
if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind {
180180
if cx.tcx.find_field_index(ident, &variant)
181-
== Some(cx.tcx.field_index(fieldpat.hir_id, cx.tables))
181+
== Some(cx.tcx.field_index(fieldpat.hir_id, cx.tables()))
182182
{
183183
cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |lint| {
184184
let mut err = lint
@@ -901,15 +901,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes {
901901
expr: &hir::Expr<'_>,
902902
) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
903903
let def = if let hir::ExprKind::Path(ref qpath) = expr.kind {
904-
cx.tables.qpath_res(qpath, expr.hir_id)
904+
cx.tables().qpath_res(qpath, expr.hir_id)
905905
} else {
906906
return None;
907907
};
908908
if let Res::Def(DefKind::Fn, did) = def {
909909
if !def_id_is_transmute(cx, did) {
910910
return None;
911911
}
912-
let sig = cx.tables.node_type(expr.hir_id).fn_sig(cx.tcx);
912+
let sig = cx.tables().node_type(expr.hir_id).fn_sig(cx.tcx);
913913
let from = sig.inputs().skip_binder()[0];
914914
let to = *sig.output().skip_binder();
915915
return Some((from, to));
@@ -1891,7 +1891,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
18911891
if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind {
18921892
// Find calls to `mem::{uninitialized,zeroed}` methods.
18931893
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
1894-
let def_id = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
1894+
let def_id = cx.tables().qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
18951895

18961896
if cx.tcx.is_diagnostic_item(sym::mem_zeroed, def_id) {
18971897
return Some(InitKind::Zeroed);
@@ -1905,14 +1905,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19051905
}
19061906
} else if let hir::ExprKind::MethodCall(_, _, ref args, _) = expr.kind {
19071907
// Find problematic calls to `MaybeUninit::assume_init`.
1908-
let def_id = cx.tables.type_dependent_def_id(expr.hir_id)?;
1908+
let def_id = cx.tables().type_dependent_def_id(expr.hir_id)?;
19091909
if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) {
19101910
// This is a call to *some* method named `assume_init`.
19111911
// See if the `self` parameter is one of the dangerous constructors.
19121912
if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind {
19131913
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
19141914
let def_id =
1915-
cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
1915+
cx.tables().qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
19161916

19171917
if cx.tcx.is_diagnostic_item(sym::maybe_uninit_zeroed, def_id) {
19181918
return Some(InitKind::Zeroed);
@@ -2025,7 +2025,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
20252025
// This conjures an instance of a type out of nothing,
20262026
// using zeroed or uninitialized memory.
20272027
// We are extremely conservative with what we warn about.
2028-
let conjured_ty = cx.tables.expr_ty(expr);
2028+
let conjured_ty = cx.tables().expr_ty(expr);
20292029
if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) {
20302030
cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| {
20312031
let mut err = lint.build(&format!(

src/librustc_lint/context.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use rustc_session::Session;
3737
use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP};
3838
use rustc_target::abi::LayoutOf;
3939

40+
use std::cell::Cell;
4041
use std::slice;
4142

4243
/// Information about the registered lints.
@@ -423,9 +424,17 @@ pub struct LateContext<'a, 'tcx> {
423424
/// Type context we're checking in.
424425
pub tcx: TyCtxt<'tcx>,
425426

426-
/// Side-tables for the body we are in.
427-
// FIXME: Make this lazy to avoid running the TypeckTables query?
428-
pub tables: &'a ty::TypeckTables<'tcx>,
427+
/// Current body, or `None` if outside a body.
428+
pub enclosing_body: Option<hir::BodyId>,
429+
430+
/// Type-checking side-tables for the current body. Access using the
431+
/// `tables` method, which handles querying the tables on demand.
432+
// FIXME(eddyb) move all the code accessing internal fields like this,
433+
// to this module, to avoid exposing it to lint logic.
434+
pub(super) cached_typeck_tables: Cell<Option<&'tcx ty::TypeckTables<'tcx>>>,
435+
436+
// HACK(eddyb) replace this with having `Option` around `&TypeckTables`.
437+
pub(super) empty_typeck_tables: &'a ty::TypeckTables<'tcx>,
429438

430439
/// Parameter environment for the item we are in.
431440
pub param_env: ty::ParamEnv<'tcx>,
@@ -667,6 +676,22 @@ impl LintContext for EarlyContext<'_> {
667676
}
668677

669678
impl<'a, 'tcx> LateContext<'a, 'tcx> {
679+
/// Gets the type-checking side-tables for the current body,
680+
/// or empty `TypeckTables` if outside a body.
681+
// FIXME(eddyb) return `Option<&'tcx ty::TypeckTables<'tcx>>`,
682+
// where `None` indicates we're outside a body.
683+
pub fn tables(&self) -> &'a ty::TypeckTables<'tcx> {
684+
if let Some(body) = self.enclosing_body {
685+
self.cached_typeck_tables.get().unwrap_or_else(|| {
686+
let tables = self.tcx.body_tables(body);
687+
self.cached_typeck_tables.set(Some(tables));
688+
tables
689+
})
690+
} else {
691+
self.empty_typeck_tables
692+
}
693+
}
694+
670695
pub fn current_lint_root(&self) -> hir::HirId {
671696
self.last_node_with_lint_attrs
672697
}

src/librustc_lint/late.rs

+29-10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use rustc_span::Span;
3030

3131
use log::debug;
3232
use std::any::Any;
33+
use std::cell::Cell;
3334
use std::slice;
3435

3536
/// Extract the `LintStore` from the query context.
@@ -104,12 +105,25 @@ impl<'a, 'tcx, T: LateLintPass<'a, 'tcx>> hir_visit::Visitor<'tcx>
104105
hir_visit::NestedVisitorMap::All(self.context.tcx.hir())
105106
}
106107

107-
fn visit_nested_body(&mut self, body: hir::BodyId) {
108-
let old_tables = self.context.tables;
109-
self.context.tables = self.context.tcx.body_tables(body);
110-
let body = self.context.tcx.hir().body(body);
108+
fn visit_nested_body(&mut self, body_id: hir::BodyId) {
109+
let old_enclosing_body = self.context.enclosing_body.replace(body_id);
110+
let old_cached_typeck_tables = self.context.cached_typeck_tables.get();
111+
112+
// HACK(eddyb) avoid trashing `cached_typeck_tables` when we're
113+
// nested in `visit_fn`, which may have already resulted in them
114+
// being queried.
115+
if old_enclosing_body != Some(body_id) {
116+
self.context.cached_typeck_tables.set(None);
117+
}
118+
119+
let body = self.context.tcx.hir().body(body_id);
111120
self.visit_body(body);
112-
self.context.tables = old_tables;
121+
self.context.enclosing_body = old_enclosing_body;
122+
123+
// See HACK comment above.
124+
if old_enclosing_body != Some(body_id) {
125+
self.context.cached_typeck_tables.set(old_cached_typeck_tables);
126+
}
113127
}
114128

115129
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
@@ -181,13 +195,14 @@ impl<'a, 'tcx, T: LateLintPass<'a, 'tcx>> hir_visit::Visitor<'tcx>
181195
) {
182196
// Wrap in tables here, not just in visit_nested_body,
183197
// in order for `check_fn` to be able to use them.
184-
let old_tables = self.context.tables;
185-
self.context.tables = self.context.tcx.body_tables(body_id);
198+
let old_enclosing_body = self.context.enclosing_body.replace(body_id);
199+
let old_cached_typeck_tables = self.context.cached_typeck_tables.take();
186200
let body = self.context.tcx.hir().body(body_id);
187201
lint_callback!(self, check_fn, fk, decl, body, span, id);
188202
hir_visit::walk_fn(self, fk, decl, body_id, span, id);
189203
lint_callback!(self, check_fn_post, fk, decl, body, span, id);
190-
self.context.tables = old_tables;
204+
self.context.enclosing_body = old_enclosing_body;
205+
self.context.cached_typeck_tables.set(old_cached_typeck_tables);
191206
}
192207

193208
fn visit_variant_data(
@@ -361,7 +376,9 @@ fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(
361376

362377
let context = LateContext {
363378
tcx,
364-
tables: &ty::TypeckTables::empty(None),
379+
enclosing_body: None,
380+
cached_typeck_tables: Cell::new(None),
381+
empty_typeck_tables: &ty::TypeckTables::empty(None),
365382
param_env: ty::ParamEnv::empty(),
366383
access_levels,
367384
lint_store: unerased_lint_store(tcx),
@@ -408,7 +425,9 @@ fn late_lint_pass_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tc
408425

409426
let context = LateContext {
410427
tcx,
411-
tables: &ty::TypeckTables::empty(None),
428+
enclosing_body: None,
429+
cached_typeck_tables: Cell::new(None),
430+
empty_typeck_tables: &ty::TypeckTables::empty(None),
412431
param_env: ty::ParamEnv::empty(),
413432
access_levels,
414433
lint_store: unerased_lint_store(tcx),

src/librustc_lint/types.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ fn report_bin_hex_error(
167167
the type `{}` and will become `{}{}`",
168168
repr_str, val, t, actually, t
169169
));
170-
if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative)
170+
if let Some(sugg_ty) =
171+
get_type_suggestion(&cx.tables().node_type(expr.hir_id), val, negative)
171172
{
172173
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
173174
let (sans_suffix, _) = repr_str.split_at(pos);
@@ -301,7 +302,7 @@ fn lint_uint_literal<'a, 'tcx>(
301302
if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) {
302303
match par_e.kind {
303304
hir::ExprKind::Cast(..) => {
304-
if let ty::Char = cx.tables.expr_ty(par_e).kind {
305+
if let ty::Char = cx.tables().expr_ty(par_e).kind {
305306
cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| {
306307
lint.build("only `u8` can be cast into `char`")
307308
.span_suggestion(
@@ -352,7 +353,7 @@ fn lint_literal<'a, 'tcx>(
352353
e: &'tcx hir::Expr<'tcx>,
353354
lit: &hir::Lit,
354355
) {
355-
match cx.tables.node_type(e.hir_id).kind {
356+
match cx.tables().node_type(e.hir_id).kind {
356357
ty::Int(t) => {
357358
match lit.node {
358359
ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
@@ -448,7 +449,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
448449
// Normalize the binop so that the literal is always on the RHS in
449450
// the comparison
450451
let norm_binop = if swap { rev_binop(binop) } else { binop };
451-
match cx.tables.node_type(expr.hir_id).kind {
452+
match cx.tables().node_type(expr.hir_id).kind {
452453
ty::Int(int_ty) => {
453454
let (min, max) = int_ty_range(int_ty);
454455
let lit_val: i128 = match lit.kind {

src/librustc_lint/unused.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
4646
return;
4747
}
4848

49-
let ty = cx.tables.expr_ty(&expr);
49+
let ty = cx.tables().expr_ty(&expr);
5050
let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, s.span, "", "", 1);
5151

5252
let mut fn_warned = false;
@@ -55,7 +55,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
5555
hir::ExprKind::Call(ref callee, _) => {
5656
match callee.kind {
5757
hir::ExprKind::Path(ref qpath) => {
58-
match cx.tables.qpath_res(qpath, callee.hir_id) {
58+
match cx.tables().qpath_res(qpath, callee.hir_id) {
5959
Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id),
6060
// `Res::Local` if it was a closure, for which we
6161
// do not currently support must-use linting
@@ -65,7 +65,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
6565
_ => None,
6666
}
6767
}
68-
hir::ExprKind::MethodCall(..) => cx.tables.type_dependent_def_id(expr.hir_id),
68+
hir::ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id),
6969
_ => None,
7070
};
7171
if let Some(def_id) = maybe_def_id {
@@ -950,7 +950,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
950950
_ => return,
951951
}
952952

953-
for adj in cx.tables.expr_adjustments(e) {
953+
for adj in cx.tables().expr_adjustments(e) {
954954
if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
955955
cx.struct_span_lint(UNUSED_ALLOCATION, e.span, |lint| {
956956
let msg = match m {

src/tools/clippy/clippy_lints/src/arithmetic.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
8686
_ => (),
8787
}
8888

89-
let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
89+
let (l_ty, r_ty) = (cx.tables().expr_ty(l), cx.tables().expr_ty(r));
9090
if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
9191
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
9292
self.expr_span = Some(expr.span);
@@ -96,8 +96,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
9696
}
9797
},
9898
hir::ExprKind::Unary(hir::UnOp::UnNeg, arg) => {
99-
let ty = cx.tables.expr_ty(arg);
100-
if constant_simple(cx, cx.tables, expr).is_none() {
99+
let ty = cx.tables().expr_ty(arg);
100+
if constant_simple(cx, cx.tables(), expr).is_none() {
101101
if ty.is_integral() {
102102
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
103103
self.expr_span = Some(expr.span);

src/tools/clippy/clippy_lints/src/assertions_on_constants.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
7272
}
7373
if_chain! {
7474
if let ExprKind::Unary(_, ref lit) = e.kind;
75-
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, lit);
75+
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables(), lit);
7676
if is_true;
7777
then {
7878
lint_true(true);
@@ -121,7 +121,7 @@ fn match_assert_with_message<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx E
121121
if let ExprKind::DropTemps(ref expr) = expr.kind;
122122
if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind;
123123
// bind the first argument of the `assert!` macro
124-
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, expr);
124+
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables(), expr);
125125
// arm 1 pattern
126126
if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind;
127127
if let ExprKind::Lit(ref lit) = lit_expr.kind;

src/tools/clippy/clippy_lints/src/assign_ops.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
8282
hir::ExprKind::Assign(assignee, e, _) => {
8383
if let hir::ExprKind::Binary(op, l, r) = &e.kind {
8484
let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
85-
let ty = cx.tables.expr_ty(assignee);
86-
let rty = cx.tables.expr_ty(rhs);
85+
let ty = cx.tables().expr_ty(assignee);
86+
let rty = cx.tables().expr_ty(rhs);
8787
macro_rules! ops {
8888
($op:expr,
8989
$cx:expr,
@@ -167,7 +167,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
167167
// a = b commutative_op a
168168
// Limited to primitive type as these ops are know to be commutative
169169
if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r)
170-
&& cx.tables.expr_ty(assignee).is_primitive_ty()
170+
&& cx.tables().expr_ty(assignee).is_primitive_ty()
171171
{
172172
match op.node {
173173
hir::BinOpKind::Add

0 commit comments

Comments
 (0)