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}
;
20 use rustc_ast
::visit
as ast_visit
;
21 use rustc_session
::lint
::{BufferedEarlyLint, LintBuffer, LintPass}
;
22 use rustc_session
::Session
;
23 use rustc_span
::symbol
::Ident
;
29 macro_rules
! run_early_pass
{ ($cx
:expr
, $f
:ident
, $
($args
:expr
),*) => ({
30 $cx
.pass
.$
f(&$cx
.context
, $
($args
),*);
33 struct EarlyContextAndPass
<'a
, T
: EarlyLintPass
> {
34 context
: EarlyContext
<'a
>,
38 impl<'a
, T
: EarlyLintPass
> EarlyContextAndPass
<'a
, T
> {
39 fn check_id(&mut self, id
: ast
::NodeId
) {
40 for early_lint
in self.context
.buffered
.take(id
) {
41 let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic }
= early_lint
;
42 self.context
.lookup_with_diagnostics(
45 |lint
| lint
.build(&msg
).emit(),
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
: ast
::NodeId
, attrs
: &'a
[ast
::Attribute
], f
: F
)
58 let is_crate_node
= id
== ast
::CRATE_NODE_ID
;
59 let push
= self.context
.builder
.push(attrs
, &self.context
.lint_store
, is_crate_node
);
61 self.enter_attrs(attrs
);
63 self.exit_attrs(attrs
);
64 self.context
.builder
.pop(push
);
67 fn enter_attrs(&mut self, attrs
: &'a
[ast
::Attribute
]) {
68 debug
!("early context: enter_attrs({:?})", attrs
);
69 run_early_pass
!(self, enter_lint_attrs
, attrs
);
72 fn exit_attrs(&mut self, attrs
: &'a
[ast
::Attribute
]) {
73 debug
!("early context: exit_attrs({:?})", attrs
);
74 run_early_pass
!(self, exit_lint_attrs
, attrs
);
78 impl<'a
, T
: EarlyLintPass
> ast_visit
::Visitor
<'a
> for EarlyContextAndPass
<'a
, T
> {
79 fn visit_param(&mut self, param
: &'a ast
::Param
) {
80 self.with_lint_attrs(param
.id
, ¶m
.attrs
, |cx
| {
81 run_early_pass
!(cx
, check_param
, param
);
82 ast_visit
::walk_param(cx
, param
);
86 fn visit_item(&mut self, it
: &'a ast
::Item
) {
87 self.with_lint_attrs(it
.id
, &it
.attrs
, |cx
| {
88 run_early_pass
!(cx
, check_item
, it
);
89 ast_visit
::walk_item(cx
, it
);
90 run_early_pass
!(cx
, check_item_post
, it
);
94 fn visit_foreign_item(&mut self, it
: &'a ast
::ForeignItem
) {
95 self.with_lint_attrs(it
.id
, &it
.attrs
, |cx
| {
96 run_early_pass
!(cx
, check_foreign_item
, it
);
97 ast_visit
::walk_foreign_item(cx
, it
);
98 run_early_pass
!(cx
, check_foreign_item_post
, it
);
102 fn visit_pat(&mut self, p
: &'a ast
::Pat
) {
103 run_early_pass
!(self, check_pat
, p
);
105 ast_visit
::walk_pat(self, p
);
106 run_early_pass
!(self, check_pat_post
, p
);
109 fn visit_anon_const(&mut self, c
: &'a ast
::AnonConst
) {
110 run_early_pass
!(self, check_anon_const
, c
);
111 ast_visit
::walk_anon_const(self, c
);
114 fn visit_expr(&mut self, e
: &'a ast
::Expr
) {
115 self.with_lint_attrs(e
.id
, &e
.attrs
, |cx
| {
116 run_early_pass
!(cx
, check_expr
, e
);
117 ast_visit
::walk_expr(cx
, e
);
121 fn visit_stmt(&mut self, s
: &'a ast
::Stmt
) {
122 run_early_pass
!(self, check_stmt
, s
);
124 ast_visit
::walk_stmt(self, s
);
127 fn visit_fn(&mut self, fk
: ast_visit
::FnKind
<'a
>, span
: Span
, id
: ast
::NodeId
) {
128 run_early_pass
!(self, check_fn
, fk
, span
, id
);
130 ast_visit
::walk_fn(self, fk
, span
);
131 run_early_pass
!(self, check_fn_post
, fk
, span
, id
);
134 fn visit_variant_data(&mut self, s
: &'a ast
::VariantData
) {
135 run_early_pass
!(self, check_struct_def
, s
);
136 if let Some(ctor_hir_id
) = s
.ctor_id() {
137 self.check_id(ctor_hir_id
);
139 ast_visit
::walk_struct_def(self, s
);
140 run_early_pass
!(self, check_struct_def_post
, s
);
143 fn visit_struct_field(&mut self, s
: &'a ast
::StructField
) {
144 self.with_lint_attrs(s
.id
, &s
.attrs
, |cx
| {
145 run_early_pass
!(cx
, check_struct_field
, s
);
146 ast_visit
::walk_struct_field(cx
, s
);
150 fn visit_variant(&mut self, v
: &'a ast
::Variant
) {
151 self.with_lint_attrs(v
.id
, &v
.attrs
, |cx
| {
152 run_early_pass
!(cx
, check_variant
, v
);
153 ast_visit
::walk_variant(cx
, v
);
154 run_early_pass
!(cx
, check_variant_post
, v
);
158 fn visit_ty(&mut self, t
: &'a ast
::Ty
) {
159 run_early_pass
!(self, check_ty
, t
);
161 ast_visit
::walk_ty(self, t
);
164 fn visit_ident(&mut self, ident
: Ident
) {
165 run_early_pass
!(self, check_ident
, ident
);
168 fn visit_mod(&mut self, m
: &'a ast
::Mod
, s
: Span
, _a
: &[ast
::Attribute
], n
: ast
::NodeId
) {
169 run_early_pass
!(self, check_mod
, m
, s
, n
);
171 ast_visit
::walk_mod(self, m
);
172 run_early_pass
!(self, check_mod_post
, m
, s
, n
);
175 fn visit_local(&mut self, l
: &'a ast
::Local
) {
176 self.with_lint_attrs(l
.id
, &l
.attrs
, |cx
| {
177 run_early_pass
!(cx
, check_local
, l
);
178 ast_visit
::walk_local(cx
, l
);
182 fn visit_block(&mut self, b
: &'a ast
::Block
) {
183 run_early_pass
!(self, check_block
, b
);
185 ast_visit
::walk_block(self, b
);
186 run_early_pass
!(self, check_block_post
, b
);
189 fn visit_arm(&mut self, a
: &'a ast
::Arm
) {
190 run_early_pass
!(self, check_arm
, a
);
191 ast_visit
::walk_arm(self, a
);
194 fn visit_expr_post(&mut self, e
: &'a ast
::Expr
) {
195 run_early_pass
!(self, check_expr_post
, e
);
198 fn visit_generic_param(&mut self, param
: &'a ast
::GenericParam
) {
199 run_early_pass
!(self, check_generic_param
, param
);
200 ast_visit
::walk_generic_param(self, param
);
203 fn visit_generics(&mut self, g
: &'a ast
::Generics
) {
204 run_early_pass
!(self, check_generics
, g
);
205 ast_visit
::walk_generics(self, g
);
208 fn visit_where_predicate(&mut self, p
: &'a ast
::WherePredicate
) {
209 run_early_pass
!(self, check_where_predicate
, p
);
210 ast_visit
::walk_where_predicate(self, p
);
213 fn visit_poly_trait_ref(&mut self, t
: &'a ast
::PolyTraitRef
, m
: &'a ast
::TraitBoundModifier
) {
214 run_early_pass
!(self, check_poly_trait_ref
, t
, m
);
215 ast_visit
::walk_poly_trait_ref(self, t
, m
);
218 fn visit_assoc_item(&mut self, item
: &'a ast
::AssocItem
, ctxt
: ast_visit
::AssocCtxt
) {
219 self.with_lint_attrs(item
.id
, &item
.attrs
, |cx
| match ctxt
{
220 ast_visit
::AssocCtxt
::Trait
=> {
221 run_early_pass
!(cx
, check_trait_item
, item
);
222 ast_visit
::walk_assoc_item(cx
, item
, ctxt
);
223 run_early_pass
!(cx
, check_trait_item_post
, item
);
225 ast_visit
::AssocCtxt
::Impl
=> {
226 run_early_pass
!(cx
, check_impl_item
, item
);
227 ast_visit
::walk_assoc_item(cx
, item
, ctxt
);
228 run_early_pass
!(cx
, check_impl_item_post
, item
);
233 fn visit_lifetime(&mut self, lt
: &'a ast
::Lifetime
) {
234 run_early_pass
!(self, check_lifetime
, lt
);
235 self.check_id(lt
.id
);
238 fn visit_path(&mut self, p
: &'a ast
::Path
, id
: ast
::NodeId
) {
239 run_early_pass
!(self, check_path
, p
, id
);
241 ast_visit
::walk_path(self, p
);
244 fn visit_attribute(&mut self, attr
: &'a ast
::Attribute
) {
245 run_early_pass
!(self, check_attribute
, attr
);
248 fn visit_mac_def(&mut self, mac
: &'a ast
::MacroDef
, id
: ast
::NodeId
) {
249 run_early_pass
!(self, check_mac_def
, mac
, id
);
253 fn visit_mac(&mut self, mac
: &'a ast
::MacCall
) {
254 // FIXME(#54110): So, this setup isn't really right. I think
255 // that (a) the librustc_ast visitor ought to be doing this as
256 // part of `walk_mac`, and (b) we should be calling
257 // `visit_path`, *but* that would require a `NodeId`, and I
258 // want to get #53686 fixed quickly. -nmatsakis
259 ast_visit
::walk_path(self, &mac
.path
);
261 run_early_pass
!(self, check_mac
, mac
);
265 struct EarlyLintPassObjects
<'a
> {
266 lints
: &'a
mut [EarlyLintPassObject
],
269 #[allow(rustc::lint_pass_impl_without_macro)]
270 impl LintPass
for EarlyLintPassObjects
<'_
> {
271 fn name(&self) -> &'
static str {
276 macro_rules
! expand_early_lint_pass_impl_methods
{
277 ([$
($
(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
278 $
(fn $
name(&mut self, context
: &EarlyContext
<'_
>, $
($param
: $arg
),*) {
279 for obj
in self.lints
.iter_mut() {
280 obj
.$
name(context
, $
($param
),*);
286 macro_rules
! early_lint_pass_impl
{
287 ([], [$
($methods
:tt
)*]) => (
288 impl EarlyLintPass
for EarlyLintPassObjects
<'_
> {
289 expand_early_lint_pass_impl_methods
!([$
($methods
)*]);
294 crate::early_lint_methods
!(early_lint_pass_impl
, []);
296 fn early_lint_crate
<T
: EarlyLintPass
>(
298 lint_store
: &LintStore
,
301 buffered
: LintBuffer
,
302 warn_about_weird_lints
: bool
,
304 let mut cx
= EarlyContextAndPass
{
305 context
: EarlyContext
::new(sess
, lint_store
, krate
, buffered
, warn_about_weird_lints
),
309 // Visit the whole crate.
310 cx
.with_lint_attrs(ast
::CRATE_NODE_ID
, &krate
.attrs
, |cx
| {
311 // since the root module isn't visited as an item (because it isn't an
312 // item), warn for it here.
313 run_early_pass
!(cx
, check_crate
, krate
);
315 ast_visit
::walk_crate(cx
, krate
);
317 run_early_pass
!(cx
, check_crate_post
, krate
);
322 pub fn check_ast_crate
<T
: EarlyLintPass
>(
324 lint_store
: &LintStore
,
327 lint_buffer
: Option
<LintBuffer
>,
331 if pre_expansion { &lint_store.pre_expansion_passes }
else { &lint_store.early_passes }
;
332 let mut passes
: Vec
<_
> = passes
.iter().map(|p
| (p
)()).collect();
333 let mut buffered
= lint_buffer
.unwrap_or_default();
335 if !sess
.opts
.debugging_opts
.no_interleave_lints
{
337 early_lint_crate(sess
, lint_store
, krate
, builtin_lints
, buffered
, pre_expansion
);
339 if !passes
.is_empty() {
340 buffered
= early_lint_crate(
344 EarlyLintPassObjects { lints: &mut passes[..] }
,
350 for pass
in &mut passes
{
352 sess
.prof
.extra_verbose_generic_activity("run_lint", pass
.name()).run(|| {
357 EarlyLintPassObjects { lints: slice::from_mut(pass) }
,
365 // All of the buffered lints should have been emitted at this point.
366 // If not, that means that we somehow buffered a lint for a node id
367 // that was not lint-checked (perhaps it doesn't exist?). This is a bug.
369 // Rustdoc runs everybody-loops before the early lints and removes
370 // function bodies, so it's totally possible for linted
371 // node ids to not exist (e.g., macros defined within functions for the
372 // unused_macro lint) anymore. So we only run this check
373 // when we're not in rustdoc mode. (see issue #47639)
374 if !sess
.opts
.actually_rustdoc
{
375 for (_id
, lints
) in buffered
.map
{
376 for early_lint
in lints
{
377 sess
.delay_span_bug(early_lint
.span
, "failed to process buffered lint here");