1 //! Implementation of lint checking.
3 //! The lint checking is mostly consolidated into one pass which runs
4 //! after all other analyses. Throughout compilation, lint warnings
5 //! can be added via the `add_lint` method on the Session structure. This
6 //! requires a span and an ID of the node that the lint is being added to. The
7 //! lint isn't actually emitted at that time because it is unknown what the
8 //! actual lint level at that location is.
10 //! To actually emit lint warnings/errors, a separate pass is used.
11 //! A context keeps track of the current state of all lint levels.
12 //! Upon entering a node of the ast which can modify the lint settings, the
13 //! previous lint state is pushed onto a stack and the ast is then recursed
14 //! upon. As the ast is traversed, this keeps track of the current lint level
15 //! for all lint attributes.
17 use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}
;
19 use rustc_data_structures
::sync
::join
;
21 use rustc_hir
::def_id
::LocalDefId
;
22 use rustc_hir
::intravisit
as hir_visit
;
23 use rustc_hir
::intravisit
::Visitor
;
24 use rustc_middle
::hir
::nested_filter
;
25 use rustc_middle
::ty
::{self, TyCtxt}
;
26 use rustc_session
::lint
::LintPass
;
32 /// Extract the `LintStore` from the query context.
33 /// This function exists because we've erased `LintStore` as `dyn Any` in the context.
34 pub fn unerased_lint_store(tcx
: TyCtxt
<'_
>) -> &LintStore
{
35 let store
: &dyn Any
= &*tcx
.lint_store
;
36 store
.downcast_ref().unwrap()
39 macro_rules
! lint_callback
{ ($cx
:expr
, $f
:ident
, $
($args
:expr
),*) => ({
40 $cx
.pass
.$
f(&$cx
.context
, $
($args
),*);
43 /// Implements the AST traversal for late lint passes. `T` provides the
44 /// `check_*` methods.
45 pub struct LateContextAndPass
<'tcx
, T
: LateLintPass
<'tcx
>> {
46 context
: LateContext
<'tcx
>,
50 impl<'tcx
, T
: LateLintPass
<'tcx
>> LateContextAndPass
<'tcx
, T
> {
51 /// Merge the lints specified by any lint attributes into the
52 /// current lint context, call the provided function, then reset the
53 /// lints in effect to their previous state.
54 fn with_lint_attrs
<F
>(&mut self, id
: hir
::HirId
, f
: F
)
58 let attrs
= self.context
.tcx
.hir().attrs(id
);
59 let prev
= self.context
.last_node_with_lint_attrs
;
60 self.context
.last_node_with_lint_attrs
= id
;
61 debug
!("late context: enter_attrs({:?})", attrs
);
62 lint_callback
!(self, enter_lint_attrs
, attrs
);
64 debug
!("late context: exit_attrs({:?})", attrs
);
65 lint_callback
!(self, exit_lint_attrs
, attrs
);
66 self.context
.last_node_with_lint_attrs
= prev
;
69 fn with_param_env
<F
>(&mut self, id
: hir
::OwnerId
, f
: F
)
73 let old_param_env
= self.context
.param_env
;
74 self.context
.param_env
= self.context
.tcx
.param_env(id
);
76 self.context
.param_env
= old_param_env
;
79 fn process_mod(&mut self, m
: &'tcx hir
::Mod
<'tcx
>, n
: hir
::HirId
) {
80 lint_callback
!(self, check_mod
, m
, n
);
81 hir_visit
::walk_mod(self, m
, n
);
85 impl<'tcx
, T
: LateLintPass
<'tcx
>> hir_visit
::Visitor
<'tcx
> for LateContextAndPass
<'tcx
, T
> {
86 type NestedFilter
= nested_filter
::All
;
88 /// Because lints are scoped lexically, we want to walk nested
89 /// items in the context of the outer item, so enable
91 fn nested_visit_map(&mut self) -> Self::Map
{
92 self.context
.tcx
.hir()
95 fn visit_nested_body(&mut self, body_id
: hir
::BodyId
) {
96 let old_enclosing_body
= self.context
.enclosing_body
.replace(body_id
);
97 let old_cached_typeck_results
= self.context
.cached_typeck_results
.get();
99 // HACK(eddyb) avoid trashing `cached_typeck_results` when we're
100 // nested in `visit_fn`, which may have already resulted in them
102 if old_enclosing_body
!= Some(body_id
) {
103 self.context
.cached_typeck_results
.set(None
);
106 let body
= self.context
.tcx
.hir().body(body_id
);
107 self.visit_body(body
);
108 self.context
.enclosing_body
= old_enclosing_body
;
110 // See HACK comment above.
111 if old_enclosing_body
!= Some(body_id
) {
112 self.context
.cached_typeck_results
.set(old_cached_typeck_results
);
116 fn visit_param(&mut self, param
: &'tcx hir
::Param
<'tcx
>) {
117 self.with_lint_attrs(param
.hir_id
, |cx
| {
118 hir_visit
::walk_param(cx
, param
);
122 fn visit_body(&mut self, body
: &'tcx hir
::Body
<'tcx
>) {
123 lint_callback
!(self, check_body
, body
);
124 hir_visit
::walk_body(self, body
);
125 lint_callback
!(self, check_body_post
, body
);
128 fn visit_item(&mut self, it
: &'tcx hir
::Item
<'tcx
>) {
129 let generics
= self.context
.generics
.take();
130 self.context
.generics
= it
.kind
.generics();
131 let old_cached_typeck_results
= self.context
.cached_typeck_results
.take();
132 let old_enclosing_body
= self.context
.enclosing_body
.take();
133 self.with_lint_attrs(it
.hir_id(), |cx
| {
134 cx
.with_param_env(it
.owner_id
, |cx
| {
135 lint_callback
!(cx
, check_item
, it
);
136 hir_visit
::walk_item(cx
, it
);
137 lint_callback
!(cx
, check_item_post
, it
);
140 self.context
.enclosing_body
= old_enclosing_body
;
141 self.context
.cached_typeck_results
.set(old_cached_typeck_results
);
142 self.context
.generics
= generics
;
145 fn visit_foreign_item(&mut self, it
: &'tcx hir
::ForeignItem
<'tcx
>) {
146 self.with_lint_attrs(it
.hir_id(), |cx
| {
147 cx
.with_param_env(it
.owner_id
, |cx
| {
148 lint_callback
!(cx
, check_foreign_item
, it
);
149 hir_visit
::walk_foreign_item(cx
, it
);
154 fn visit_pat(&mut self, p
: &'tcx hir
::Pat
<'tcx
>) {
155 lint_callback
!(self, check_pat
, p
);
156 hir_visit
::walk_pat(self, p
);
159 fn visit_expr(&mut self, e
: &'tcx hir
::Expr
<'tcx
>) {
160 self.with_lint_attrs(e
.hir_id
, |cx
| {
161 lint_callback
!(cx
, check_expr
, e
);
162 hir_visit
::walk_expr(cx
, e
);
163 lint_callback
!(cx
, check_expr_post
, e
);
167 fn visit_stmt(&mut self, s
: &'tcx hir
::Stmt
<'tcx
>) {
168 // See `EarlyContextAndPass::visit_stmt` for an explanation
169 // of why we call `walk_stmt` outside of `with_lint_attrs`
170 self.with_lint_attrs(s
.hir_id
, |cx
| {
171 lint_callback
!(cx
, check_stmt
, s
);
173 hir_visit
::walk_stmt(self, s
);
178 fk
: hir_visit
::FnKind
<'tcx
>,
179 decl
: &'tcx hir
::FnDecl
<'tcx
>,
180 body_id
: hir
::BodyId
,
184 // Wrap in typeck results here, not just in visit_nested_body,
185 // in order for `check_fn` to be able to use them.
186 let old_enclosing_body
= self.context
.enclosing_body
.replace(body_id
);
187 let old_cached_typeck_results
= self.context
.cached_typeck_results
.take();
188 let body
= self.context
.tcx
.hir().body(body_id
);
189 lint_callback
!(self, check_fn
, fk
, decl
, body
, span
, id
);
190 hir_visit
::walk_fn(self, fk
, decl
, body_id
, id
);
191 self.context
.enclosing_body
= old_enclosing_body
;
192 self.context
.cached_typeck_results
.set(old_cached_typeck_results
);
195 fn visit_variant_data(&mut self, s
: &'tcx hir
::VariantData
<'tcx
>) {
196 lint_callback
!(self, check_struct_def
, s
);
197 hir_visit
::walk_struct_def(self, s
);
200 fn visit_field_def(&mut self, s
: &'tcx hir
::FieldDef
<'tcx
>) {
201 self.with_lint_attrs(s
.hir_id
, |cx
| {
202 lint_callback
!(cx
, check_field_def
, s
);
203 hir_visit
::walk_field_def(cx
, s
);
207 fn visit_variant(&mut self, v
: &'tcx hir
::Variant
<'tcx
>) {
208 self.with_lint_attrs(v
.hir_id
, |cx
| {
209 lint_callback
!(cx
, check_variant
, v
);
210 hir_visit
::walk_variant(cx
, v
);
214 fn visit_ty(&mut self, t
: &'tcx hir
::Ty
<'tcx
>) {
215 lint_callback
!(self, check_ty
, t
);
216 hir_visit
::walk_ty(self, t
);
219 fn visit_infer(&mut self, inf
: &'tcx hir
::InferArg
) {
220 hir_visit
::walk_inf(self, inf
);
223 fn visit_mod(&mut self, m
: &'tcx hir
::Mod
<'tcx
>, _
: Span
, n
: hir
::HirId
) {
224 if !self.context
.only_module
{
225 self.process_mod(m
, n
);
229 fn visit_local(&mut self, l
: &'tcx hir
::Local
<'tcx
>) {
230 self.with_lint_attrs(l
.hir_id
, |cx
| {
231 lint_callback
!(cx
, check_local
, l
);
232 hir_visit
::walk_local(cx
, l
);
236 fn visit_block(&mut self, b
: &'tcx hir
::Block
<'tcx
>) {
237 lint_callback
!(self, check_block
, b
);
238 hir_visit
::walk_block(self, b
);
239 lint_callback
!(self, check_block_post
, b
);
242 fn visit_arm(&mut self, a
: &'tcx hir
::Arm
<'tcx
>) {
243 lint_callback
!(self, check_arm
, a
);
244 hir_visit
::walk_arm(self, a
);
247 fn visit_generic_param(&mut self, p
: &'tcx hir
::GenericParam
<'tcx
>) {
248 lint_callback
!(self, check_generic_param
, p
);
249 hir_visit
::walk_generic_param(self, p
);
252 fn visit_generics(&mut self, g
: &'tcx hir
::Generics
<'tcx
>) {
253 lint_callback
!(self, check_generics
, g
);
254 hir_visit
::walk_generics(self, g
);
257 fn visit_where_predicate(&mut self, p
: &'tcx hir
::WherePredicate
<'tcx
>) {
258 hir_visit
::walk_where_predicate(self, p
);
261 fn visit_poly_trait_ref(&mut self, t
: &'tcx hir
::PolyTraitRef
<'tcx
>) {
262 lint_callback
!(self, check_poly_trait_ref
, t
);
263 hir_visit
::walk_poly_trait_ref(self, t
);
266 fn visit_trait_item(&mut self, trait_item
: &'tcx hir
::TraitItem
<'tcx
>) {
267 let generics
= self.context
.generics
.take();
268 self.context
.generics
= Some(&trait_item
.generics
);
269 self.with_lint_attrs(trait_item
.hir_id(), |cx
| {
270 cx
.with_param_env(trait_item
.owner_id
, |cx
| {
271 lint_callback
!(cx
, check_trait_item
, trait_item
);
272 hir_visit
::walk_trait_item(cx
, trait_item
);
275 self.context
.generics
= generics
;
278 fn visit_impl_item(&mut self, impl_item
: &'tcx hir
::ImplItem
<'tcx
>) {
279 let generics
= self.context
.generics
.take();
280 self.context
.generics
= Some(&impl_item
.generics
);
281 self.with_lint_attrs(impl_item
.hir_id(), |cx
| {
282 cx
.with_param_env(impl_item
.owner_id
, |cx
| {
283 lint_callback
!(cx
, check_impl_item
, impl_item
);
284 hir_visit
::walk_impl_item(cx
, impl_item
);
285 lint_callback
!(cx
, check_impl_item_post
, impl_item
);
288 self.context
.generics
= generics
;
291 fn visit_lifetime(&mut self, lt
: &'tcx hir
::Lifetime
) {
292 hir_visit
::walk_lifetime(self, lt
);
295 fn visit_path(&mut self, p
: &hir
::Path
<'tcx
>, id
: hir
::HirId
) {
296 lint_callback
!(self, check_path
, p
, id
);
297 hir_visit
::walk_path(self, p
);
300 fn visit_attribute(&mut self, attr
: &'tcx ast
::Attribute
) {
301 lint_callback
!(self, check_attribute
, attr
);
305 // Combines multiple lint passes into a single pass, at runtime. Each
306 // `check_foo` method in `$methods` within this pass simply calls `check_foo`
307 // once per `$pass`. Compare with `declare_combined_late_lint_pass`, which is
308 // similar, but combines lint passes at compile time.
309 struct RuntimeCombinedLateLintPass
<'a
, 'tcx
> {
310 passes
: &'a
mut [LateLintPassObject
<'tcx
>],
313 #[allow(rustc::lint_pass_impl_without_macro)]
314 impl LintPass
for RuntimeCombinedLateLintPass
<'_
, '_
> {
315 fn name(&self) -> &'
static str {
320 macro_rules
! impl_late_lint_pass
{
321 ([], [$
($
(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => {
322 impl<'tcx
> LateLintPass
<'tcx
> for RuntimeCombinedLateLintPass
<'_
, 'tcx
> {
323 $
(fn $
f(&mut self, context
: &LateContext
<'tcx
>, $
($param
: $arg
),*) {
324 for pass
in self.passes
.iter_mut() {
325 pass
.$
f(context
, $
($param
),*);
332 crate::late_lint_methods
!(impl_late_lint_pass
, []);
334 pub(super) fn late_lint_mod
<'tcx
, T
: LateLintPass
<'tcx
> + 'tcx
>(
336 module_def_id
: LocalDefId
,
339 let context
= LateContext
{
341 enclosing_body
: None
,
342 cached_typeck_results
: Cell
::new(None
),
343 param_env
: ty
::ParamEnv
::empty(),
344 effective_visibilities
: &tcx
.effective_visibilities(()),
345 lint_store
: unerased_lint_store(tcx
),
346 last_node_with_lint_attrs
: tcx
.hir().local_def_id_to_hir_id(module_def_id
),
351 // Note: `passes` is often empty. In that case, it's faster to run
352 // `builtin_lints` directly rather than bundling it up into the
353 // `RuntimeCombinedLateLintPass`.
354 let mut passes
: Vec
<_
> =
355 unerased_lint_store(tcx
).late_module_passes
.iter().map(|mk_pass
| (mk_pass
)(tcx
)).collect();
356 if passes
.is_empty() {
357 late_lint_mod_inner(tcx
, module_def_id
, context
, builtin_lints
);
359 passes
.push(Box
::new(builtin_lints
));
360 let pass
= RuntimeCombinedLateLintPass { passes: &mut passes[..] }
;
361 late_lint_mod_inner(tcx
, module_def_id
, context
, pass
);
365 fn late_lint_mod_inner
<'tcx
, T
: LateLintPass
<'tcx
>>(
367 module_def_id
: LocalDefId
,
368 context
: LateContext
<'tcx
>,
371 let mut cx
= LateContextAndPass { context, pass }
;
373 let (module
, _span
, hir_id
) = tcx
.hir().get_module(module_def_id
);
374 cx
.process_mod(module
, hir_id
);
376 // Visit the crate attributes
377 if hir_id
== hir
::CRATE_HIR_ID
{
378 for attr
in tcx
.hir().attrs(hir
::CRATE_HIR_ID
).iter() {
379 cx
.visit_attribute(attr
)
384 fn late_lint_crate
<'tcx
, T
: LateLintPass
<'tcx
> + 'tcx
>(tcx
: TyCtxt
<'tcx
>, builtin_lints
: T
) {
385 let context
= LateContext
{
387 enclosing_body
: None
,
388 cached_typeck_results
: Cell
::new(None
),
389 param_env
: ty
::ParamEnv
::empty(),
390 effective_visibilities
: &tcx
.effective_visibilities(()),
391 lint_store
: unerased_lint_store(tcx
),
392 last_node_with_lint_attrs
: hir
::CRATE_HIR_ID
,
397 // Note: `passes` is often empty. In that case, it's faster to run
398 // `builtin_lints` directly rather than bundling it up into the
399 // `RuntimeCombinedLateLintPass`.
400 let mut passes
: Vec
<_
> =
401 unerased_lint_store(tcx
).late_passes
.iter().map(|mk_pass
| (mk_pass
)(tcx
)).collect();
402 if passes
.is_empty() {
403 late_lint_crate_inner(tcx
, context
, builtin_lints
);
405 passes
.push(Box
::new(builtin_lints
));
406 let pass
= RuntimeCombinedLateLintPass { passes: &mut passes[..] }
;
407 late_lint_crate_inner(tcx
, context
, pass
);
411 fn late_lint_crate_inner
<'tcx
, T
: LateLintPass
<'tcx
>>(
413 context
: LateContext
<'tcx
>,
416 let mut cx
= LateContextAndPass { context, pass }
;
418 // Visit the whole crate.
419 cx
.with_lint_attrs(hir
::CRATE_HIR_ID
, |cx
| {
420 // Since the root module isn't visited as an item (because it isn't an
421 // item), warn for it here.
422 lint_callback
!(cx
, check_crate
,);
423 tcx
.hir().walk_toplevel_module(cx
);
424 tcx
.hir().walk_attributes(cx
);
425 lint_callback
!(cx
, check_crate_post
,);
429 /// Performs lint checking on a crate.
430 pub fn check_crate
<'tcx
, T
: LateLintPass
<'tcx
> + 'tcx
>(
432 builtin_lints
: impl FnOnce() -> T
+ Send
,
436 tcx
.sess
.time("crate_lints", || {
437 // Run whole crate non-incremental lints
438 late_lint_crate(tcx
, builtin_lints());
442 tcx
.sess
.time("module_lints", || {
443 // Run per-module lints
444 tcx
.hir().par_for_each_module(|module
| tcx
.ensure().lint_mod(module
));