]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | //! Implementation of lint checking. |
2 | //! | |
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. | |
9 | //! | |
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. | |
16 | ||
17 | use crate::context::{EarlyContext, LintContext, LintStore}; | |
18 | use crate::passes::{EarlyLintPass, EarlyLintPassObject}; | |
5099ac24 FG |
19 | use rustc_ast::ptr::P; |
20 | use rustc_ast::visit::{self as ast_visit, Visitor}; | |
04454e1e | 21 | use rustc_ast::{self as ast, walk_list, HasAttrs}; |
5099ac24 | 22 | use rustc_middle::ty::RegisteredTools; |
ba9703b0 | 23 | use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; |
dfeec247 | 24 | use rustc_session::Session; |
f9f354fc | 25 | use rustc_span::symbol::Ident; |
dfeec247 | 26 | use rustc_span::Span; |
dfeec247 | 27 | |
dfeec247 | 28 | use std::slice; |
3dfed10e | 29 | use tracing::debug; |
dfeec247 XL |
30 | |
31 | macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({ | |
32 | $cx.pass.$f(&$cx.context, $($args),*); | |
33 | }) } | |
34 | ||
5099ac24 | 35 | pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { |
dfeec247 XL |
36 | context: EarlyContext<'a>, |
37 | pass: T, | |
38 | } | |
39 | ||
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) { | |
ba9703b0 | 43 | let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; |
74b04a01 XL |
44 | self.context.lookup_with_diagnostics( |
45 | lint_id.lint, | |
46 | Some(span), | |
5e7ed085 FG |
47 | |lint| { |
48 | lint.build(&msg).emit(); | |
49 | }, | |
74b04a01 | 50 | diagnostic, |
dfeec247 XL |
51 | ); |
52 | } | |
53 | } | |
54 | ||
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) | |
59 | where | |
60 | F: FnOnce(&mut Self), | |
61 | { | |
f035d41b | 62 | let is_crate_node = id == ast::CRATE_NODE_ID; |
5e7ed085 FG |
63 | let push = self.context.builder.push(attrs, is_crate_node, None); |
64 | ||
dfeec247 | 65 | self.check_id(id); |
dfeec247 XL |
66 | debug!("early context: enter_attrs({:?})", attrs); |
67 | run_early_pass!(self, enter_lint_attrs, attrs); | |
923072b8 | 68 | f(self); |
dfeec247 XL |
69 | debug!("early context: exit_attrs({:?})", attrs); |
70 | run_early_pass!(self, exit_lint_attrs, attrs); | |
923072b8 | 71 | self.context.builder.pop(push); |
dfeec247 XL |
72 | } |
73 | } | |
74 | ||
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); | |
80 | }); | |
81 | } | |
82 | ||
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); | |
88 | }) | |
89 | } | |
90 | ||
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); | |
96 | }) | |
97 | } | |
98 | ||
99 | fn visit_pat(&mut self, p: &'a ast::Pat) { | |
100 | run_early_pass!(self, check_pat, p); | |
101 | self.check_id(p.id); | |
102 | ast_visit::walk_pat(self, p); | |
103 | run_early_pass!(self, check_pat_post, p); | |
104 | } | |
105 | ||
ba9703b0 XL |
106 | fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { |
107 | run_early_pass!(self, check_anon_const, c); | |
cdc7bbd5 | 108 | self.check_id(c.id); |
ba9703b0 XL |
109 | ast_visit::walk_anon_const(self, c); |
110 | } | |
111 | ||
dfeec247 XL |
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); | |
116 | }) | |
117 | } | |
118 | ||
136023e0 XL |
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); | |
122 | }) | |
123 | } | |
124 | ||
dfeec247 | 125 | fn visit_stmt(&mut self, s: &'a ast::Stmt) { |
29967ef6 XL |
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 | |
131 | // | |
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); | |
136 | cx.check_id(s.id); | |
137 | }); | |
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 | |
dfeec247 XL |
142 | ast_visit::walk_stmt(self, s); |
143 | } | |
144 | ||
74b04a01 XL |
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); | |
dfeec247 | 147 | self.check_id(id); |
74b04a01 | 148 | ast_visit::walk_fn(self, fk, span); |
5869c6ff XL |
149 | |
150 | // Explicitly check for lints associated with 'closure_id', since | |
151 | // it does not have a corresponding AST node | |
04454e1e | 152 | if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk { |
5869c6ff XL |
153 | if let ast::Async::Yes { closure_id, .. } = sig.header.asyncness { |
154 | self.check_id(closure_id); | |
155 | } | |
156 | } | |
74b04a01 | 157 | run_early_pass!(self, check_fn_post, fk, span, id); |
dfeec247 XL |
158 | } |
159 | ||
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); | |
164 | } | |
165 | ast_visit::walk_struct_def(self, s); | |
166 | run_early_pass!(self, check_struct_def_post, s); | |
167 | } | |
168 | ||
6a06907d | 169 | fn visit_field_def(&mut self, s: &'a ast::FieldDef) { |
dfeec247 | 170 | self.with_lint_attrs(s.id, &s.attrs, |cx| { |
6a06907d XL |
171 | run_early_pass!(cx, check_field_def, s); |
172 | ast_visit::walk_field_def(cx, s); | |
dfeec247 XL |
173 | }) |
174 | } | |
175 | ||
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); | |
181 | }) | |
182 | } | |
183 | ||
184 | fn visit_ty(&mut self, t: &'a ast::Ty) { | |
185 | run_early_pass!(self, check_ty, t); | |
186 | self.check_id(t.id); | |
187 | ast_visit::walk_ty(self, t); | |
188 | } | |
189 | ||
f9f354fc | 190 | fn visit_ident(&mut self, ident: Ident) { |
dfeec247 XL |
191 | run_early_pass!(self, check_ident, ident); |
192 | } | |
193 | ||
dfeec247 XL |
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); | |
198 | }) | |
199 | } | |
200 | ||
201 | fn visit_block(&mut self, b: &'a ast::Block) { | |
202 | run_early_pass!(self, check_block, b); | |
203 | self.check_id(b.id); | |
204 | ast_visit::walk_block(self, b); | |
205 | run_early_pass!(self, check_block_post, b); | |
206 | } | |
207 | ||
208 | fn visit_arm(&mut self, a: &'a ast::Arm) { | |
136023e0 XL |
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); | |
212 | }) | |
dfeec247 XL |
213 | } |
214 | ||
215 | fn visit_expr_post(&mut self, e: &'a ast::Expr) { | |
216 | run_early_pass!(self, check_expr_post, e); | |
5869c6ff XL |
217 | |
218 | // Explicitly check for lints associated with 'closure_id', since | |
219 | // it does not have a corresponding AST node | |
220 | match e.kind { | |
221 | ast::ExprKind::Closure(_, ast::Async::Yes { closure_id, .. }, ..) | |
222 | | ast::ExprKind::Async(_, closure_id, ..) => self.check_id(closure_id), | |
223 | _ => {} | |
224 | } | |
dfeec247 XL |
225 | } |
226 | ||
29967ef6 XL |
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); | |
230 | } | |
231 | ||
dfeec247 XL |
232 | fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { |
233 | run_early_pass!(self, check_generic_param, param); | |
923072b8 | 234 | self.check_id(param.id); |
dfeec247 XL |
235 | ast_visit::walk_generic_param(self, param); |
236 | } | |
237 | ||
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); | |
241 | } | |
242 | ||
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); | |
246 | } | |
247 | ||
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); | |
251 | } | |
252 | ||
74b04a01 XL |
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); | |
259 | } | |
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); | |
264 | } | |
dfeec247 XL |
265 | }); |
266 | } | |
267 | ||
923072b8 | 268 | fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { |
dfeec247 XL |
269 | run_early_pass!(self, check_lifetime, lt); |
270 | self.check_id(lt.id); | |
271 | } | |
272 | ||
273 | fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { | |
274 | run_early_pass!(self, check_path, p, id); | |
275 | self.check_id(id); | |
276 | ast_visit::walk_path(self, p); | |
277 | } | |
278 | ||
04454e1e FG |
279 | fn visit_path_segment(&mut self, path_span: Span, s: &'a ast::PathSegment) { |
280 | self.check_id(s.id); | |
281 | ast_visit::walk_path_segment(self, path_span, s); | |
282 | } | |
283 | ||
dfeec247 XL |
284 | fn visit_attribute(&mut self, attr: &'a ast::Attribute) { |
285 | run_early_pass!(self, check_attribute, attr); | |
286 | } | |
287 | ||
288 | fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { | |
289 | run_early_pass!(self, check_mac_def, mac, id); | |
290 | self.check_id(id); | |
291 | } | |
292 | ||
29967ef6 | 293 | fn visit_mac_call(&mut self, mac: &'a ast::MacCall) { |
dfeec247 | 294 | run_early_pass!(self, check_mac, mac); |
29967ef6 | 295 | ast_visit::walk_mac(self, mac); |
dfeec247 XL |
296 | } |
297 | } | |
298 | ||
299 | struct EarlyLintPassObjects<'a> { | |
300 | lints: &'a mut [EarlyLintPassObject], | |
301 | } | |
302 | ||
303 | #[allow(rustc::lint_pass_impl_without_macro)] | |
304 | impl LintPass for EarlyLintPassObjects<'_> { | |
305 | fn name(&self) -> &'static str { | |
306 | panic!() | |
307 | } | |
308 | } | |
309 | ||
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),*); | |
315 | } | |
316 | })* | |
317 | ) | |
318 | } | |
319 | ||
320 | macro_rules! early_lint_pass_impl { | |
321 | ([], [$($methods:tt)*]) => ( | |
322 | impl EarlyLintPass for EarlyLintPassObjects<'_> { | |
323 | expand_early_lint_pass_impl_methods!([$($methods)*]); | |
324 | } | |
325 | ) | |
326 | } | |
327 | ||
328 | crate::early_lint_methods!(early_lint_pass_impl, []); | |
329 | ||
5099ac24 FG |
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] | |
335 | where | |
336 | 'a: 'b; | |
337 | fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) | |
338 | where | |
339 | 'a: 'b; | |
340 | } | |
341 | ||
342 | impl<'a> EarlyCheckNode<'a> for &'a ast::Crate { | |
343 | fn id(self) -> ast::NodeId { | |
344 | ast::CRATE_NODE_ID | |
345 | } | |
346 | fn attrs<'b>(self) -> &'b [ast::Attribute] | |
347 | where | |
348 | 'a: 'b, | |
349 | { | |
350 | &self.attrs | |
351 | } | |
352 | fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) | |
353 | where | |
354 | 'a: 'b, | |
355 | { | |
356 | run_early_pass!(cx, check_crate, self); | |
357 | ast_visit::walk_crate(cx, self); | |
358 | run_early_pass!(cx, check_crate_post, self); | |
359 | } | |
360 | } | |
361 | ||
362 | impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::Item>]) { | |
363 | fn id(self) -> ast::NodeId { | |
364 | self.0 | |
365 | } | |
366 | fn attrs<'b>(self) -> &'b [ast::Attribute] | |
367 | where | |
368 | 'a: 'b, | |
369 | { | |
370 | self.1 | |
371 | } | |
372 | fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) | |
373 | where | |
374 | 'a: 'b, | |
375 | { | |
376 | walk_list!(cx, visit_attribute, self.1); | |
377 | walk_list!(cx, visit_item, self.2); | |
378 | } | |
379 | } | |
380 | ||
381 | fn early_lint_node<'a>( | |
dfeec247 | 382 | sess: &Session, |
5099ac24 | 383 | warn_about_weird_lints: bool, |
dfeec247 | 384 | lint_store: &LintStore, |
5099ac24 | 385 | registered_tools: &RegisteredTools, |
dfeec247 | 386 | buffered: LintBuffer, |
5099ac24 FG |
387 | pass: impl EarlyLintPass, |
388 | check_node: impl EarlyCheckNode<'a>, | |
dfeec247 XL |
389 | ) -> LintBuffer { |
390 | let mut cx = EarlyContextAndPass { | |
c295e0f8 XL |
391 | context: EarlyContext::new( |
392 | sess, | |
5099ac24 | 393 | warn_about_weird_lints, |
c295e0f8 | 394 | lint_store, |
5099ac24 | 395 | registered_tools, |
c295e0f8 | 396 | buffered, |
c295e0f8 | 397 | ), |
dfeec247 XL |
398 | pass, |
399 | }; | |
400 | ||
5099ac24 | 401 | cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); |
dfeec247 XL |
402 | cx.context.buffered |
403 | } | |
404 | ||
5099ac24 | 405 | pub fn check_ast_node<'a>( |
dfeec247 | 406 | sess: &Session, |
dfeec247 | 407 | pre_expansion: bool, |
5099ac24 FG |
408 | lint_store: &LintStore, |
409 | registered_tools: &RegisteredTools, | |
dfeec247 | 410 | lint_buffer: Option<LintBuffer>, |
5099ac24 FG |
411 | builtin_lints: impl EarlyLintPass, |
412 | check_node: impl EarlyCheckNode<'a>, | |
dfeec247 | 413 | ) { |
ba9703b0 XL |
414 | let passes = |
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(); | |
dfeec247 XL |
417 | let mut buffered = lint_buffer.unwrap_or_default(); |
418 | ||
923072b8 FG |
419 | if sess.opts.debugging_opts.no_interleave_lints { |
420 | for (i, pass) in passes.iter_mut().enumerate() { | |
421 | buffered = | |
422 | sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| { | |
423 | early_lint_node( | |
424 | sess, | |
425 | !pre_expansion && i == 0, | |
426 | lint_store, | |
427 | registered_tools, | |
428 | buffered, | |
429 | EarlyLintPassObjects { lints: slice::from_mut(pass) }, | |
430 | check_node, | |
431 | ) | |
432 | }); | |
433 | } | |
434 | } else { | |
5099ac24 | 435 | buffered = early_lint_node( |
c295e0f8 | 436 | sess, |
923072b8 | 437 | !pre_expansion, |
c295e0f8 | 438 | lint_store, |
5099ac24 | 439 | registered_tools, |
c295e0f8 | 440 | buffered, |
5099ac24 FG |
441 | builtin_lints, |
442 | check_node, | |
c295e0f8 | 443 | ); |
dfeec247 XL |
444 | |
445 | if !passes.is_empty() { | |
5099ac24 | 446 | buffered = early_lint_node( |
dfeec247 | 447 | sess, |
5099ac24 | 448 | false, |
dfeec247 | 449 | lint_store, |
5099ac24 | 450 | registered_tools, |
dfeec247 | 451 | buffered, |
5099ac24 FG |
452 | EarlyLintPassObjects { lints: &mut passes[..] }, |
453 | check_node, | |
dfeec247 XL |
454 | ); |
455 | } | |
dfeec247 XL |
456 | } |
457 | ||
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. | |
136023e0 | 461 | for (id, lints) in buffered.map { |
5869c6ff | 462 | for early_lint in lints { |
136023e0 XL |
463 | sess.delay_span_bug( |
464 | early_lint.span, | |
465 | &format!( | |
466 | "failed to process buffered lint here (dummy = {})", | |
467 | id == ast::DUMMY_NODE_ID | |
468 | ), | |
469 | ); | |
dfeec247 XL |
470 | } |
471 | } | |
472 | } |