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_ast
::walk_list
;
20 use rustc_data_structures
::sync
::{join, par_iter, ParallelIterator}
;
22 use rustc_hir
::def_id
::{LocalDefId, LOCAL_CRATE}
;
23 use rustc_hir
::intravisit
as hir_visit
;
24 use rustc_hir
::intravisit
::Visitor
;
25 use rustc_middle
::hir
::map
::Map
;
26 use rustc_middle
::ty
::{self, TyCtxt}
;
27 use rustc_session
::lint
::LintPass
;
28 use rustc_span
::symbol
::Symbol
;
36 /// Extract the `LintStore` from the query context.
37 /// This function exists because we've erased `LintStore` as `dyn Any` in the context.
38 crate fn unerased_lint_store(tcx
: TyCtxt
<'_
>) -> &LintStore
{
39 let store
: &dyn Any
= &*tcx
.lint_store
;
40 store
.downcast_ref().unwrap()
43 macro_rules
! lint_callback
{ ($cx
:expr
, $f
:ident
, $
($args
:expr
),*) => ({
44 $cx
.pass
.$
f(&$cx
.context
, $
($args
),*);
47 struct LateContextAndPass
<'tcx
, T
: LateLintPass
<'tcx
>> {
48 context
: LateContext
<'tcx
>,
52 impl<'tcx
, T
: LateLintPass
<'tcx
>> LateContextAndPass
<'tcx
, T
> {
53 /// Merge the lints specified by any lint attributes into the
54 /// current lint context, call the provided function, then reset the
55 /// lints in effect to their previous state.
56 fn with_lint_attrs
<F
>(&mut self, id
: hir
::HirId
, attrs
: &'tcx
[ast
::Attribute
], f
: F
)
60 let prev
= self.context
.last_node_with_lint_attrs
;
61 self.context
.last_node_with_lint_attrs
= id
;
62 self.enter_attrs(attrs
);
64 self.exit_attrs(attrs
);
65 self.context
.last_node_with_lint_attrs
= prev
;
68 fn with_param_env
<F
>(&mut self, id
: hir
::HirId
, f
: F
)
72 let old_param_env
= self.context
.param_env
;
73 self.context
.param_env
=
74 self.context
.tcx
.param_env(self.context
.tcx
.hir().local_def_id(id
));
76 self.context
.param_env
= old_param_env
;
79 fn process_mod(&mut self, m
: &'tcx hir
::Mod
<'tcx
>, s
: Span
, n
: hir
::HirId
) {
80 lint_callback
!(self, check_mod
, m
, s
, n
);
81 hir_visit
::walk_mod(self, m
, n
);
82 lint_callback
!(self, check_mod_post
, m
, s
, n
);
85 fn enter_attrs(&mut self, attrs
: &'tcx
[ast
::Attribute
]) {
86 debug
!("late context: enter_attrs({:?})", attrs
);
87 lint_callback
!(self, enter_lint_attrs
, attrs
);
90 fn exit_attrs(&mut self, attrs
: &'tcx
[ast
::Attribute
]) {
91 debug
!("late context: exit_attrs({:?})", attrs
);
92 lint_callback
!(self, exit_lint_attrs
, attrs
);
96 impl<'tcx
, T
: LateLintPass
<'tcx
>> hir_visit
::Visitor
<'tcx
> for LateContextAndPass
<'tcx
, T
> {
99 /// Because lints are scoped lexically, we want to walk nested
100 /// items in the context of the outer item, so enable
102 fn nested_visit_map(&mut self) -> hir_visit
::NestedVisitorMap
<Self::Map
> {
103 hir_visit
::NestedVisitorMap
::All(self.context
.tcx
.hir())
106 fn visit_nested_body(&mut self, body_id
: hir
::BodyId
) {
107 let old_enclosing_body
= self.context
.enclosing_body
.replace(body_id
);
108 let old_cached_typeck_tables
= self.context
.cached_typeck_tables
.get();
110 // HACK(eddyb) avoid trashing `cached_typeck_tables` when we're
111 // nested in `visit_fn`, which may have already resulted in them
113 if old_enclosing_body
!= Some(body_id
) {
114 self.context
.cached_typeck_tables
.set(None
);
117 let body
= self.context
.tcx
.hir().body(body_id
);
118 self.visit_body(body
);
119 self.context
.enclosing_body
= old_enclosing_body
;
121 // See HACK comment above.
122 if old_enclosing_body
!= Some(body_id
) {
123 self.context
.cached_typeck_tables
.set(old_cached_typeck_tables
);
127 fn visit_param(&mut self, param
: &'tcx hir
::Param
<'tcx
>) {
128 self.with_lint_attrs(param
.hir_id
, ¶m
.attrs
, |cx
| {
129 lint_callback
!(cx
, check_param
, param
);
130 hir_visit
::walk_param(cx
, param
);
134 fn visit_body(&mut self, body
: &'tcx hir
::Body
<'tcx
>) {
135 lint_callback
!(self, check_body
, body
);
136 hir_visit
::walk_body(self, body
);
137 lint_callback
!(self, check_body_post
, body
);
140 fn visit_item(&mut self, it
: &'tcx hir
::Item
<'tcx
>) {
141 let generics
= self.context
.generics
.take();
142 self.context
.generics
= it
.kind
.generics();
143 self.with_lint_attrs(it
.hir_id
, &it
.attrs
, |cx
| {
144 cx
.with_param_env(it
.hir_id
, |cx
| {
145 lint_callback
!(cx
, check_item
, it
);
146 hir_visit
::walk_item(cx
, it
);
147 lint_callback
!(cx
, check_item_post
, it
);
150 self.context
.generics
= generics
;
153 fn visit_foreign_item(&mut self, it
: &'tcx hir
::ForeignItem
<'tcx
>) {
154 self.with_lint_attrs(it
.hir_id
, &it
.attrs
, |cx
| {
155 cx
.with_param_env(it
.hir_id
, |cx
| {
156 lint_callback
!(cx
, check_foreign_item
, it
);
157 hir_visit
::walk_foreign_item(cx
, it
);
158 lint_callback
!(cx
, check_foreign_item_post
, it
);
163 fn visit_pat(&mut self, p
: &'tcx hir
::Pat
<'tcx
>) {
164 lint_callback
!(self, check_pat
, p
);
165 hir_visit
::walk_pat(self, p
);
168 fn visit_expr(&mut self, e
: &'tcx hir
::Expr
<'tcx
>) {
169 self.with_lint_attrs(e
.hir_id
, &e
.attrs
, |cx
| {
170 lint_callback
!(cx
, check_expr
, e
);
171 hir_visit
::walk_expr(cx
, e
);
172 lint_callback
!(cx
, check_expr_post
, e
);
176 fn visit_stmt(&mut self, s
: &'tcx hir
::Stmt
<'tcx
>) {
177 // statement attributes are actually just attributes on one of
181 // so we keep track of lint levels there
182 lint_callback
!(self, check_stmt
, s
);
183 hir_visit
::walk_stmt(self, s
);
188 fk
: hir_visit
::FnKind
<'tcx
>,
189 decl
: &'tcx hir
::FnDecl
<'tcx
>,
190 body_id
: hir
::BodyId
,
194 // Wrap in tables here, not just in visit_nested_body,
195 // in order for `check_fn` to be able to use them.
196 let old_enclosing_body
= self.context
.enclosing_body
.replace(body_id
);
197 let old_cached_typeck_tables
= self.context
.cached_typeck_tables
.take();
198 let body
= self.context
.tcx
.hir().body(body_id
);
199 lint_callback
!(self, check_fn
, fk
, decl
, body
, span
, id
);
200 hir_visit
::walk_fn(self, fk
, decl
, body_id
, span
, id
);
201 lint_callback
!(self, check_fn_post
, fk
, decl
, body
, span
, id
);
202 self.context
.enclosing_body
= old_enclosing_body
;
203 self.context
.cached_typeck_tables
.set(old_cached_typeck_tables
);
206 fn visit_variant_data(
208 s
: &'tcx hir
::VariantData
<'tcx
>,
210 _
: &'tcx hir
::Generics
<'tcx
>,
214 lint_callback
!(self, check_struct_def
, s
);
215 hir_visit
::walk_struct_def(self, s
);
216 lint_callback
!(self, check_struct_def_post
, s
);
219 fn visit_struct_field(&mut self, s
: &'tcx hir
::StructField
<'tcx
>) {
220 self.with_lint_attrs(s
.hir_id
, &s
.attrs
, |cx
| {
221 lint_callback
!(cx
, check_struct_field
, s
);
222 hir_visit
::walk_struct_field(cx
, s
);
228 v
: &'tcx hir
::Variant
<'tcx
>,
229 g
: &'tcx hir
::Generics
<'tcx
>,
232 self.with_lint_attrs(v
.id
, &v
.attrs
, |cx
| {
233 lint_callback
!(cx
, check_variant
, v
);
234 hir_visit
::walk_variant(cx
, v
, g
, item_id
);
235 lint_callback
!(cx
, check_variant_post
, v
);
239 fn visit_ty(&mut self, t
: &'tcx hir
::Ty
<'tcx
>) {
240 lint_callback
!(self, check_ty
, t
);
241 hir_visit
::walk_ty(self, t
);
244 fn visit_name(&mut self, sp
: Span
, name
: Symbol
) {
245 lint_callback
!(self, check_name
, sp
, name
);
248 fn visit_mod(&mut self, m
: &'tcx hir
::Mod
<'tcx
>, s
: Span
, n
: hir
::HirId
) {
249 if !self.context
.only_module
{
250 self.process_mod(m
, s
, n
);
254 fn visit_local(&mut self, l
: &'tcx hir
::Local
<'tcx
>) {
255 self.with_lint_attrs(l
.hir_id
, &l
.attrs
, |cx
| {
256 lint_callback
!(cx
, check_local
, l
);
257 hir_visit
::walk_local(cx
, l
);
261 fn visit_block(&mut self, b
: &'tcx hir
::Block
<'tcx
>) {
262 lint_callback
!(self, check_block
, b
);
263 hir_visit
::walk_block(self, b
);
264 lint_callback
!(self, check_block_post
, b
);
267 fn visit_arm(&mut self, a
: &'tcx hir
::Arm
<'tcx
>) {
268 lint_callback
!(self, check_arm
, a
);
269 hir_visit
::walk_arm(self, a
);
272 fn visit_generic_param(&mut self, p
: &'tcx hir
::GenericParam
<'tcx
>) {
273 lint_callback
!(self, check_generic_param
, p
);
274 hir_visit
::walk_generic_param(self, p
);
277 fn visit_generics(&mut self, g
: &'tcx hir
::Generics
<'tcx
>) {
278 lint_callback
!(self, check_generics
, g
);
279 hir_visit
::walk_generics(self, g
);
282 fn visit_where_predicate(&mut self, p
: &'tcx hir
::WherePredicate
<'tcx
>) {
283 lint_callback
!(self, check_where_predicate
, p
);
284 hir_visit
::walk_where_predicate(self, p
);
287 fn visit_poly_trait_ref(
289 t
: &'tcx hir
::PolyTraitRef
<'tcx
>,
290 m
: hir
::TraitBoundModifier
,
292 lint_callback
!(self, check_poly_trait_ref
, t
, m
);
293 hir_visit
::walk_poly_trait_ref(self, t
, m
);
296 fn visit_trait_item(&mut self, trait_item
: &'tcx hir
::TraitItem
<'tcx
>) {
297 let generics
= self.context
.generics
.take();
298 self.context
.generics
= Some(&trait_item
.generics
);
299 self.with_lint_attrs(trait_item
.hir_id
, &trait_item
.attrs
, |cx
| {
300 cx
.with_param_env(trait_item
.hir_id
, |cx
| {
301 lint_callback
!(cx
, check_trait_item
, trait_item
);
302 hir_visit
::walk_trait_item(cx
, trait_item
);
303 lint_callback
!(cx
, check_trait_item_post
, trait_item
);
306 self.context
.generics
= generics
;
309 fn visit_impl_item(&mut self, impl_item
: &'tcx hir
::ImplItem
<'tcx
>) {
310 let generics
= self.context
.generics
.take();
311 self.context
.generics
= Some(&impl_item
.generics
);
312 self.with_lint_attrs(impl_item
.hir_id
, &impl_item
.attrs
, |cx
| {
313 cx
.with_param_env(impl_item
.hir_id
, |cx
| {
314 lint_callback
!(cx
, check_impl_item
, impl_item
);
315 hir_visit
::walk_impl_item(cx
, impl_item
);
316 lint_callback
!(cx
, check_impl_item_post
, impl_item
);
319 self.context
.generics
= generics
;
322 fn visit_lifetime(&mut self, lt
: &'tcx hir
::Lifetime
) {
323 lint_callback
!(self, check_lifetime
, lt
);
324 hir_visit
::walk_lifetime(self, lt
);
327 fn visit_path(&mut self, p
: &'tcx hir
::Path
<'tcx
>, id
: hir
::HirId
) {
328 lint_callback
!(self, check_path
, p
, id
);
329 hir_visit
::walk_path(self, p
);
332 fn visit_attribute(&mut self, attr
: &'tcx ast
::Attribute
) {
333 lint_callback
!(self, check_attribute
, attr
);
337 struct LateLintPassObjects
<'a
> {
338 lints
: &'a
mut [LateLintPassObject
],
341 #[allow(rustc::lint_pass_impl_without_macro)]
342 impl LintPass
for LateLintPassObjects
<'_
> {
343 fn name(&self) -> &'
static str {
348 macro_rules
! expand_late_lint_pass_impl_methods
{
349 ([$hir
:tt
], [$
($
(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
350 $
(fn $
name(&mut self, context
: &LateContext
<$hir
>, $
($param
: $arg
),*) {
351 for obj
in self.lints
.iter_mut() {
352 obj
.$
name(context
, $
($param
),*);
358 macro_rules
! late_lint_pass_impl
{
359 ([], [$hir
:tt
], $methods
:tt
) => {
360 impl<$hir
> LateLintPass
<$hir
> for LateLintPassObjects
<'_
> {
361 expand_late_lint_pass_impl_methods
!([$hir
], $methods
);
366 crate::late_lint_methods
!(late_lint_pass_impl
, [], ['tcx
]);
368 fn late_lint_mod_pass
<'tcx
, T
: LateLintPass
<'tcx
>>(
370 module_def_id
: LocalDefId
,
373 let access_levels
= &tcx
.privacy_access_levels(LOCAL_CRATE
);
375 let context
= LateContext
{
377 enclosing_body
: None
,
378 cached_typeck_tables
: Cell
::new(None
),
379 param_env
: ty
::ParamEnv
::empty(),
381 lint_store
: unerased_lint_store(tcx
),
382 last_node_with_lint_attrs
: tcx
.hir().as_local_hir_id(module_def_id
),
387 let mut cx
= LateContextAndPass { context, pass }
;
389 let (module
, span
, hir_id
) = tcx
.hir().get_module(module_def_id
);
390 cx
.process_mod(module
, span
, hir_id
);
392 // Visit the crate attributes
393 if hir_id
== hir
::CRATE_HIR_ID
{
394 walk_list
!(cx
, visit_attribute
, tcx
.hir().attrs(hir
::CRATE_HIR_ID
));
398 pub fn late_lint_mod
<'tcx
, T
: LateLintPass
<'tcx
>>(
400 module_def_id
: LocalDefId
,
403 if tcx
.sess
.opts
.debugging_opts
.no_interleave_lints
{
404 // These passes runs in late_lint_crate with -Z no_interleave_lints
408 late_lint_mod_pass(tcx
, module_def_id
, builtin_lints
);
410 let mut passes
: Vec
<_
> =
411 unerased_lint_store(tcx
).late_module_passes
.iter().map(|pass
| (pass
)()).collect();
413 if !passes
.is_empty() {
414 late_lint_mod_pass(tcx
, module_def_id
, LateLintPassObjects { lints: &mut passes[..] }
);
418 fn late_lint_pass_crate
<'tcx
, T
: LateLintPass
<'tcx
>>(tcx
: TyCtxt
<'tcx
>, pass
: T
) {
419 let access_levels
= &tcx
.privacy_access_levels(LOCAL_CRATE
);
421 let krate
= tcx
.hir().krate();
423 let context
= LateContext
{
425 enclosing_body
: None
,
426 cached_typeck_tables
: Cell
::new(None
),
427 param_env
: ty
::ParamEnv
::empty(),
429 lint_store
: unerased_lint_store(tcx
),
430 last_node_with_lint_attrs
: hir
::CRATE_HIR_ID
,
435 let mut cx
= LateContextAndPass { context, pass }
;
437 // Visit the whole crate.
438 cx
.with_lint_attrs(hir
::CRATE_HIR_ID
, &krate
.item
.attrs
, |cx
| {
439 // since the root module isn't visited as an item (because it isn't an
440 // item), warn for it here.
441 lint_callback
!(cx
, check_crate
, krate
);
443 hir_visit
::walk_crate(cx
, krate
);
445 lint_callback
!(cx
, check_crate_post
, krate
);
449 fn late_lint_crate
<'tcx
, T
: LateLintPass
<'tcx
>>(tcx
: TyCtxt
<'tcx
>, builtin_lints
: T
) {
450 let mut passes
= unerased_lint_store(tcx
).late_passes
.iter().map(|p
| (p
)()).collect
::<Vec
<_
>>();
452 if !tcx
.sess
.opts
.debugging_opts
.no_interleave_lints
{
453 if !passes
.is_empty() {
454 late_lint_pass_crate(tcx
, LateLintPassObjects { lints: &mut passes[..] }
);
457 late_lint_pass_crate(tcx
, builtin_lints
);
459 for pass
in &mut passes
{
460 tcx
.sess
.prof
.extra_verbose_generic_activity("run_late_lint", pass
.name()).run(|| {
461 late_lint_pass_crate(tcx
, LateLintPassObjects { lints: slice::from_mut(pass) }
);
465 let mut passes
: Vec
<_
> =
466 unerased_lint_store(tcx
).late_module_passes
.iter().map(|pass
| (pass
)()).collect();
468 for pass
in &mut passes
{
469 tcx
.sess
.prof
.extra_verbose_generic_activity("run_late_module_lint", pass
.name()).run(
471 late_lint_pass_crate(tcx
, LateLintPassObjects { lints: slice::from_mut(pass) }
);
478 /// Performs lint checking on a crate.
479 pub fn check_crate
<'tcx
, T
: LateLintPass
<'tcx
>>(
481 builtin_lints
: impl FnOnce() -> T
+ Send
,
485 tcx
.sess
.time("crate_lints", || {
486 // Run whole crate non-incremental lints
487 late_lint_crate(tcx
, builtin_lints());
491 tcx
.sess
.time("module_lints", || {
492 // Run per-module lints
493 par_iter(&tcx
.hir().krate().modules
).for_each(|(&module
, _
)| {
494 tcx
.ensure().lint_mod(tcx
.hir().local_def_id(module
));