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::context
::{EarlyContext, LintContext, LintStore}
;
18 use crate::passes
::{EarlyLintPass, EarlyLintPassObject}
;
19 use rustc_ast
::ptr
::P
;
20 use rustc_ast
::visit
::{self as ast_visit, Visitor}
;
21 use rustc_ast
::{self as ast, walk_list, HasAttrs}
;
22 use rustc_middle
::ty
::RegisteredTools
;
23 use rustc_session
::lint
::{BufferedEarlyLint, LintBuffer, LintPass}
;
24 use rustc_session
::Session
;
25 use rustc_span
::symbol
::Ident
;
31 macro_rules
! run_early_pass
{ ($cx
:expr
, $f
:ident
, $
($args
:expr
),*) => ({
32 $cx
.pass
.$
f(&$cx
.context
, $
($args
),*);
35 pub struct EarlyContextAndPass
<'a
, T
: EarlyLintPass
> {
36 context
: EarlyContext
<'a
>,
40 impl<'a
, T
: EarlyLintPass
> EarlyContextAndPass
<'a
, T
> {
41 fn check_id(&mut self, id
: ast
::NodeId
) {
42 for early_lint
in self.context
.buffered
.take(id
) {
43 let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic }
= early_lint
;
44 self.context
.lookup_with_diagnostics(
48 lint
.build(&msg
).emit();
55 /// Merge the lints specified by any lint attributes into the
56 /// current lint context, call the provided function, then reset the
57 /// lints in effect to their previous state.
58 fn with_lint_attrs
<F
>(&mut self, id
: ast
::NodeId
, attrs
: &'a
[ast
::Attribute
], f
: F
)
62 let is_crate_node
= id
== ast
::CRATE_NODE_ID
;
63 let push
= self.context
.builder
.push(attrs
, is_crate_node
, None
);
66 debug
!("early context: enter_attrs({:?})", attrs
);
67 run_early_pass
!(self, enter_lint_attrs
, attrs
);
69 debug
!("early context: exit_attrs({:?})", attrs
);
70 run_early_pass
!(self, exit_lint_attrs
, attrs
);
71 self.context
.builder
.pop(push
);
75 impl<'a
, T
: EarlyLintPass
> ast_visit
::Visitor
<'a
> for EarlyContextAndPass
<'a
, T
> {
76 fn visit_param(&mut self, param
: &'a ast
::Param
) {
77 self.with_lint_attrs(param
.id
, ¶m
.attrs
, |cx
| {
78 run_early_pass
!(cx
, check_param
, param
);
79 ast_visit
::walk_param(cx
, param
);
83 fn visit_item(&mut self, it
: &'a ast
::Item
) {
84 self.with_lint_attrs(it
.id
, &it
.attrs
, |cx
| {
85 run_early_pass
!(cx
, check_item
, it
);
86 ast_visit
::walk_item(cx
, it
);
87 run_early_pass
!(cx
, check_item_post
, it
);
91 fn visit_foreign_item(&mut self, it
: &'a ast
::ForeignItem
) {
92 self.with_lint_attrs(it
.id
, &it
.attrs
, |cx
| {
93 run_early_pass
!(cx
, check_foreign_item
, it
);
94 ast_visit
::walk_foreign_item(cx
, it
);
95 run_early_pass
!(cx
, check_foreign_item_post
, it
);
99 fn visit_pat(&mut self, p
: &'a ast
::Pat
) {
100 run_early_pass
!(self, check_pat
, p
);
102 ast_visit
::walk_pat(self, p
);
103 run_early_pass
!(self, check_pat_post
, p
);
106 fn visit_anon_const(&mut self, c
: &'a ast
::AnonConst
) {
107 run_early_pass
!(self, check_anon_const
, c
);
109 ast_visit
::walk_anon_const(self, c
);
112 fn visit_expr(&mut self, e
: &'a ast
::Expr
) {
113 self.with_lint_attrs(e
.id
, &e
.attrs
, |cx
| {
114 run_early_pass
!(cx
, check_expr
, e
);
115 ast_visit
::walk_expr(cx
, e
);
119 fn visit_expr_field(&mut self, f
: &'a ast
::ExprField
) {
120 self.with_lint_attrs(f
.id
, &f
.attrs
, |cx
| {
121 ast_visit
::walk_expr_field(cx
, f
);
125 fn visit_stmt(&mut self, s
: &'a ast
::Stmt
) {
126 // Add the statement's lint attributes to our
127 // current state when checking the statement itself.
128 // This allows us to handle attributes like
129 // `#[allow(unused_doc_comments)]`, which apply to
130 // sibling attributes on the same target
132 // Note that statements get their attributes from
133 // the AST struct that they wrap (e.g. an item)
134 self.with_lint_attrs(s
.id
, s
.attrs(), |cx
| {
135 run_early_pass
!(cx
, check_stmt
, s
);
138 // The visitor for the AST struct wrapped
139 // by the statement (e.g. `Item`) will call
140 // `with_lint_attrs`, so do this walk
141 // outside of the above `with_lint_attrs` call
142 ast_visit
::walk_stmt(self, s
);
145 fn visit_fn(&mut self, fk
: ast_visit
::FnKind
<'a
>, span
: Span
, id
: ast
::NodeId
) {
146 run_early_pass
!(self, check_fn
, fk
, span
, id
);
148 ast_visit
::walk_fn(self, fk
, span
);
150 // Explicitly check for lints associated with 'closure_id', since
151 // it does not have a corresponding AST node
152 if let ast_visit
::FnKind
::Fn(_
, _
, sig
, _
, _
, _
) = fk
{
153 if let ast
::Async
::Yes { closure_id, .. }
= sig
.header
.asyncness
{
154 self.check_id(closure_id
);
157 run_early_pass
!(self, check_fn_post
, fk
, span
, id
);
160 fn visit_variant_data(&mut self, s
: &'a ast
::VariantData
) {
161 run_early_pass
!(self, check_struct_def
, s
);
162 if let Some(ctor_hir_id
) = s
.ctor_id() {
163 self.check_id(ctor_hir_id
);
165 ast_visit
::walk_struct_def(self, s
);
166 run_early_pass
!(self, check_struct_def_post
, s
);
169 fn visit_field_def(&mut self, s
: &'a ast
::FieldDef
) {
170 self.with_lint_attrs(s
.id
, &s
.attrs
, |cx
| {
171 run_early_pass
!(cx
, check_field_def
, s
);
172 ast_visit
::walk_field_def(cx
, s
);
176 fn visit_variant(&mut self, v
: &'a ast
::Variant
) {
177 self.with_lint_attrs(v
.id
, &v
.attrs
, |cx
| {
178 run_early_pass
!(cx
, check_variant
, v
);
179 ast_visit
::walk_variant(cx
, v
);
180 run_early_pass
!(cx
, check_variant_post
, v
);
184 fn visit_ty(&mut self, t
: &'a ast
::Ty
) {
185 run_early_pass
!(self, check_ty
, t
);
187 ast_visit
::walk_ty(self, t
);
190 fn visit_ident(&mut self, ident
: Ident
) {
191 run_early_pass
!(self, check_ident
, ident
);
194 fn visit_local(&mut self, l
: &'a ast
::Local
) {
195 self.with_lint_attrs(l
.id
, &l
.attrs
, |cx
| {
196 run_early_pass
!(cx
, check_local
, l
);
197 ast_visit
::walk_local(cx
, l
);
201 fn visit_block(&mut self, b
: &'a ast
::Block
) {
202 run_early_pass
!(self, check_block
, b
);
204 ast_visit
::walk_block(self, b
);
205 run_early_pass
!(self, check_block_post
, b
);
208 fn visit_arm(&mut self, a
: &'a ast
::Arm
) {
209 self.with_lint_attrs(a
.id
, &a
.attrs
, |cx
| {
210 run_early_pass
!(cx
, check_arm
, a
);
211 ast_visit
::walk_arm(cx
, a
);
215 fn visit_expr_post(&mut self, e
: &'a ast
::Expr
) {
216 run_early_pass
!(self, check_expr_post
, e
);
218 // Explicitly check for lints associated with 'closure_id', since
219 // it does not have a corresponding AST node
221 ast
::ExprKind
::Closure(_
, ast
::Async
::Yes { closure_id, .. }
, ..)
222 | ast
::ExprKind
::Async(_
, closure_id
, ..) => self.check_id(closure_id
),
227 fn visit_generic_arg(&mut self, arg
: &'a ast
::GenericArg
) {
228 run_early_pass
!(self, check_generic_arg
, arg
);
229 ast_visit
::walk_generic_arg(self, arg
);
232 fn visit_generic_param(&mut self, param
: &'a ast
::GenericParam
) {
233 run_early_pass
!(self, check_generic_param
, param
);
234 self.check_id(param
.id
);
235 ast_visit
::walk_generic_param(self, param
);
238 fn visit_generics(&mut self, g
: &'a ast
::Generics
) {
239 run_early_pass
!(self, check_generics
, g
);
240 ast_visit
::walk_generics(self, g
);
243 fn visit_where_predicate(&mut self, p
: &'a ast
::WherePredicate
) {
244 run_early_pass
!(self, check_where_predicate
, p
);
245 ast_visit
::walk_where_predicate(self, p
);
248 fn visit_poly_trait_ref(&mut self, t
: &'a ast
::PolyTraitRef
, m
: &'a ast
::TraitBoundModifier
) {
249 run_early_pass
!(self, check_poly_trait_ref
, t
, m
);
250 ast_visit
::walk_poly_trait_ref(self, t
, m
);
253 fn visit_assoc_item(&mut self, item
: &'a ast
::AssocItem
, ctxt
: ast_visit
::AssocCtxt
) {
254 self.with_lint_attrs(item
.id
, &item
.attrs
, |cx
| match ctxt
{
255 ast_visit
::AssocCtxt
::Trait
=> {
256 run_early_pass
!(cx
, check_trait_item
, item
);
257 ast_visit
::walk_assoc_item(cx
, item
, ctxt
);
258 run_early_pass
!(cx
, check_trait_item_post
, item
);
260 ast_visit
::AssocCtxt
::Impl
=> {
261 run_early_pass
!(cx
, check_impl_item
, item
);
262 ast_visit
::walk_assoc_item(cx
, item
, ctxt
);
263 run_early_pass
!(cx
, check_impl_item_post
, item
);
268 fn visit_lifetime(&mut self, lt
: &'a ast
::Lifetime
, _
: ast_visit
::LifetimeCtxt
) {
269 run_early_pass
!(self, check_lifetime
, lt
);
270 self.check_id(lt
.id
);
273 fn visit_path(&mut self, p
: &'a ast
::Path
, id
: ast
::NodeId
) {
274 run_early_pass
!(self, check_path
, p
, id
);
276 ast_visit
::walk_path(self, p
);
279 fn visit_path_segment(&mut self, path_span
: Span
, s
: &'a ast
::PathSegment
) {
281 ast_visit
::walk_path_segment(self, path_span
, s
);
284 fn visit_attribute(&mut self, attr
: &'a ast
::Attribute
) {
285 run_early_pass
!(self, check_attribute
, attr
);
288 fn visit_mac_def(&mut self, mac
: &'a ast
::MacroDef
, id
: ast
::NodeId
) {
289 run_early_pass
!(self, check_mac_def
, mac
, id
);
293 fn visit_mac_call(&mut self, mac
: &'a ast
::MacCall
) {
294 run_early_pass
!(self, check_mac
, mac
);
295 ast_visit
::walk_mac(self, mac
);
299 struct EarlyLintPassObjects
<'a
> {
300 lints
: &'a
mut [EarlyLintPassObject
],
303 #[allow(rustc::lint_pass_impl_without_macro)]
304 impl LintPass
for EarlyLintPassObjects
<'_
> {
305 fn name(&self) -> &'
static str {
310 macro_rules
! expand_early_lint_pass_impl_methods
{
311 ([$
($
(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
312 $
(fn $
name(&mut self, context
: &EarlyContext
<'_
>, $
($param
: $arg
),*) {
313 for obj
in self.lints
.iter_mut() {
314 obj
.$
name(context
, $
($param
),*);
320 macro_rules
! early_lint_pass_impl
{
321 ([], [$
($methods
:tt
)*]) => (
322 impl EarlyLintPass
for EarlyLintPassObjects
<'_
> {
323 expand_early_lint_pass_impl_methods
!([$
($methods
)*]);
328 crate::early_lint_methods
!(early_lint_pass_impl
, []);
330 /// Early lints work on different nodes - either on the crate root, or on freshly loaded modules.
331 /// This trait generalizes over those nodes.
332 pub trait EarlyCheckNode
<'a
>: Copy
{
333 fn id(self) -> ast
::NodeId
;
334 fn attrs
<'b
>(self) -> &'b
[ast
::Attribute
]
337 fn check
<'b
>(self, cx
: &mut EarlyContextAndPass
<'b
, impl EarlyLintPass
>)
342 impl<'a
> EarlyCheckNode
<'a
> for &'a ast
::Crate
{
343 fn id(self) -> ast
::NodeId
{
346 fn attrs
<'b
>(self) -> &'b
[ast
::Attribute
]
352 fn check
<'b
>(self, cx
: &mut EarlyContextAndPass
<'b
, impl EarlyLintPass
>)
356 run_early_pass
!(cx
, check_crate
, self);
357 ast_visit
::walk_crate(cx
, self);
358 run_early_pass
!(cx
, check_crate_post
, self);
362 impl<'a
> EarlyCheckNode
<'a
> for (ast
::NodeId
, &'a
[ast
::Attribute
], &'a
[P
<ast
::Item
>]) {
363 fn id(self) -> ast
::NodeId
{
366 fn attrs
<'b
>(self) -> &'b
[ast
::Attribute
]
372 fn check
<'b
>(self, cx
: &mut EarlyContextAndPass
<'b
, impl EarlyLintPass
>)
376 walk_list
!(cx
, visit_attribute
, self.1);
377 walk_list
!(cx
, visit_item
, self.2);
381 fn early_lint_node
<'a
>(
383 warn_about_weird_lints
: bool
,
384 lint_store
: &LintStore
,
385 registered_tools
: &RegisteredTools
,
386 buffered
: LintBuffer
,
387 pass
: impl EarlyLintPass
,
388 check_node
: impl EarlyCheckNode
<'a
>,
390 let mut cx
= EarlyContextAndPass
{
391 context
: EarlyContext
::new(
393 warn_about_weird_lints
,
401 cx
.with_lint_attrs(check_node
.id(), check_node
.attrs(), |cx
| check_node
.check(cx
));
405 pub fn check_ast_node
<'a
>(
408 lint_store
: &LintStore
,
409 registered_tools
: &RegisteredTools
,
410 lint_buffer
: Option
<LintBuffer
>,
411 builtin_lints
: impl EarlyLintPass
,
412 check_node
: impl EarlyCheckNode
<'a
>,
415 if pre_expansion { &lint_store.pre_expansion_passes }
else { &lint_store.early_passes }
;
416 let mut passes
: Vec
<_
> = passes
.iter().map(|p
| (p
)()).collect();
417 let mut buffered
= lint_buffer
.unwrap_or_default();
419 if sess
.opts
.debugging_opts
.no_interleave_lints
{
420 for (i
, pass
) in passes
.iter_mut().enumerate() {
422 sess
.prof
.extra_verbose_generic_activity("run_lint", pass
.name()).run(|| {
425 !pre_expansion
&& i
== 0,
429 EarlyLintPassObjects { lints: slice::from_mut(pass) }
,
435 buffered
= early_lint_node(
445 if !passes
.is_empty() {
446 buffered
= early_lint_node(
452 EarlyLintPassObjects { lints: &mut passes[..] }
,
458 // All of the buffered lints should have been emitted at this point.
459 // If not, that means that we somehow buffered a lint for a node id
460 // that was not lint-checked (perhaps it doesn't exist?). This is a bug.
461 for (id
, lints
) in buffered
.map
{
462 for early_lint
in lints
{
466 "failed to process buffered lint here (dummy = {})",
467 id
== ast
::DUMMY_NODE_ID