]>
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}; |
9c376795 | 22 | use rustc_data_structures::stack::ensure_sufficient_stack; |
5099ac24 | 23 | use rustc_middle::ty::RegisteredTools; |
9c376795 | 24 | use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; |
dfeec247 | 25 | use rustc_session::Session; |
f9f354fc | 26 | use rustc_span::symbol::Ident; |
dfeec247 | 27 | use rustc_span::Span; |
dfeec247 | 28 | |
9c376795 FG |
29 | macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ |
30 | $cx.pass.$f(&$cx.context, $($args),*); | |
dfeec247 XL |
31 | }) } |
32 | ||
9c376795 FG |
33 | /// Implements the AST traversal for early lint passes. `T` provides the |
34 | /// `check_*` methods. | |
35 | pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { | |
dfeec247 | 36 | context: EarlyContext<'a>, |
9c376795 | 37 | pass: T, |
dfeec247 XL |
38 | } |
39 | ||
9c376795 FG |
40 | impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { |
41 | // This always-inlined function is for the hot call site. | |
42 | #[inline(always)] | |
43 | #[allow(rustc::diagnostic_outside_of_impl)] | |
44 | fn inlined_check_id(&mut self, id: ast::NodeId) { | |
dfeec247 | 45 | for early_lint in self.context.buffered.take(id) { |
ba9703b0 | 46 | let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; |
74b04a01 XL |
47 | self.context.lookup_with_diagnostics( |
48 | lint_id.lint, | |
49 | Some(span), | |
2b03887a FG |
50 | msg, |
51 | |lint| lint, | |
74b04a01 | 52 | diagnostic, |
dfeec247 XL |
53 | ); |
54 | } | |
55 | } | |
56 | ||
9c376795 FG |
57 | // This non-inlined function is for the cold call sites. |
58 | fn check_id(&mut self, id: ast::NodeId) { | |
59 | self.inlined_check_id(id) | |
60 | } | |
61 | ||
dfeec247 XL |
62 | /// Merge the lints specified by any lint attributes into the |
63 | /// current lint context, call the provided function, then reset the | |
64 | /// lints in effect to their previous state. | |
65 | fn with_lint_attrs<F>(&mut self, id: ast::NodeId, attrs: &'a [ast::Attribute], f: F) | |
66 | where | |
67 | F: FnOnce(&mut Self), | |
68 | { | |
f035d41b | 69 | let is_crate_node = id == ast::CRATE_NODE_ID; |
2b03887a | 70 | debug!(?id); |
5e7ed085 FG |
71 | let push = self.context.builder.push(attrs, is_crate_node, None); |
72 | ||
9c376795 | 73 | self.inlined_check_id(id); |
dfeec247 | 74 | debug!("early context: enter_attrs({:?})", attrs); |
9c376795 FG |
75 | lint_callback!(self, enter_lint_attrs, attrs); |
76 | ensure_sufficient_stack(|| f(self)); | |
dfeec247 | 77 | debug!("early context: exit_attrs({:?})", attrs); |
9c376795 | 78 | lint_callback!(self, exit_lint_attrs, attrs); |
923072b8 | 79 | self.context.builder.pop(push); |
dfeec247 XL |
80 | } |
81 | } | |
82 | ||
9c376795 | 83 | impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> { |
dfeec247 XL |
84 | fn visit_param(&mut self, param: &'a ast::Param) { |
85 | self.with_lint_attrs(param.id, ¶m.attrs, |cx| { | |
9c376795 | 86 | lint_callback!(cx, check_param, param); |
dfeec247 XL |
87 | ast_visit::walk_param(cx, param); |
88 | }); | |
89 | } | |
90 | ||
91 | fn visit_item(&mut self, it: &'a ast::Item) { | |
92 | self.with_lint_attrs(it.id, &it.attrs, |cx| { | |
9c376795 | 93 | lint_callback!(cx, check_item, it); |
dfeec247 | 94 | ast_visit::walk_item(cx, it); |
9c376795 | 95 | lint_callback!(cx, check_item_post, it); |
dfeec247 XL |
96 | }) |
97 | } | |
98 | ||
99 | fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) { | |
100 | self.with_lint_attrs(it.id, &it.attrs, |cx| { | |
dfeec247 | 101 | ast_visit::walk_foreign_item(cx, it); |
dfeec247 XL |
102 | }) |
103 | } | |
104 | ||
105 | fn visit_pat(&mut self, p: &'a ast::Pat) { | |
9c376795 | 106 | lint_callback!(self, check_pat, p); |
dfeec247 XL |
107 | self.check_id(p.id); |
108 | ast_visit::walk_pat(self, p); | |
9c376795 | 109 | lint_callback!(self, check_pat_post, p); |
dfeec247 XL |
110 | } |
111 | ||
f2b60f7d FG |
112 | fn visit_pat_field(&mut self, field: &'a ast::PatField) { |
113 | self.with_lint_attrs(field.id, &field.attrs, |cx| { | |
114 | ast_visit::walk_pat_field(cx, field); | |
115 | }); | |
116 | } | |
117 | ||
ba9703b0 | 118 | fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { |
cdc7bbd5 | 119 | self.check_id(c.id); |
ba9703b0 XL |
120 | ast_visit::walk_anon_const(self, c); |
121 | } | |
122 | ||
dfeec247 XL |
123 | fn visit_expr(&mut self, e: &'a ast::Expr) { |
124 | self.with_lint_attrs(e.id, &e.attrs, |cx| { | |
9c376795 | 125 | lint_callback!(cx, check_expr, e); |
dfeec247 XL |
126 | ast_visit::walk_expr(cx, e); |
127 | }) | |
128 | } | |
129 | ||
136023e0 XL |
130 | fn visit_expr_field(&mut self, f: &'a ast::ExprField) { |
131 | self.with_lint_attrs(f.id, &f.attrs, |cx| { | |
132 | ast_visit::walk_expr_field(cx, f); | |
133 | }) | |
134 | } | |
135 | ||
dfeec247 | 136 | fn visit_stmt(&mut self, s: &'a ast::Stmt) { |
29967ef6 XL |
137 | // Add the statement's lint attributes to our |
138 | // current state when checking the statement itself. | |
139 | // This allows us to handle attributes like | |
140 | // `#[allow(unused_doc_comments)]`, which apply to | |
141 | // sibling attributes on the same target | |
142 | // | |
143 | // Note that statements get their attributes from | |
144 | // the AST struct that they wrap (e.g. an item) | |
145 | self.with_lint_attrs(s.id, s.attrs(), |cx| { | |
9c376795 | 146 | lint_callback!(cx, check_stmt, s); |
29967ef6 XL |
147 | cx.check_id(s.id); |
148 | }); | |
149 | // The visitor for the AST struct wrapped | |
150 | // by the statement (e.g. `Item`) will call | |
151 | // `with_lint_attrs`, so do this walk | |
152 | // outside of the above `with_lint_attrs` call | |
dfeec247 XL |
153 | ast_visit::walk_stmt(self, s); |
154 | } | |
155 | ||
74b04a01 | 156 | fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) { |
9c376795 | 157 | lint_callback!(self, check_fn, fk, span, id); |
dfeec247 | 158 | self.check_id(id); |
f2b60f7d | 159 | ast_visit::walk_fn(self, fk); |
5869c6ff XL |
160 | |
161 | // Explicitly check for lints associated with 'closure_id', since | |
162 | // it does not have a corresponding AST node | |
04454e1e | 163 | if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk { |
5869c6ff XL |
164 | if let ast::Async::Yes { closure_id, .. } = sig.header.asyncness { |
165 | self.check_id(closure_id); | |
166 | } | |
167 | } | |
dfeec247 XL |
168 | } |
169 | ||
170 | fn visit_variant_data(&mut self, s: &'a ast::VariantData) { | |
487cf647 FG |
171 | if let Some(ctor_node_id) = s.ctor_node_id() { |
172 | self.check_id(ctor_node_id); | |
dfeec247 XL |
173 | } |
174 | ast_visit::walk_struct_def(self, s); | |
dfeec247 XL |
175 | } |
176 | ||
6a06907d | 177 | fn visit_field_def(&mut self, s: &'a ast::FieldDef) { |
dfeec247 | 178 | self.with_lint_attrs(s.id, &s.attrs, |cx| { |
6a06907d | 179 | ast_visit::walk_field_def(cx, s); |
dfeec247 XL |
180 | }) |
181 | } | |
182 | ||
183 | fn visit_variant(&mut self, v: &'a ast::Variant) { | |
184 | self.with_lint_attrs(v.id, &v.attrs, |cx| { | |
9c376795 | 185 | lint_callback!(cx, check_variant, v); |
dfeec247 | 186 | ast_visit::walk_variant(cx, v); |
dfeec247 XL |
187 | }) |
188 | } | |
189 | ||
190 | fn visit_ty(&mut self, t: &'a ast::Ty) { | |
9c376795 | 191 | lint_callback!(self, check_ty, t); |
dfeec247 XL |
192 | self.check_id(t.id); |
193 | ast_visit::walk_ty(self, t); | |
194 | } | |
195 | ||
f9f354fc | 196 | fn visit_ident(&mut self, ident: Ident) { |
9c376795 | 197 | lint_callback!(self, check_ident, ident); |
dfeec247 XL |
198 | } |
199 | ||
dfeec247 XL |
200 | fn visit_local(&mut self, l: &'a ast::Local) { |
201 | self.with_lint_attrs(l.id, &l.attrs, |cx| { | |
9c376795 | 202 | lint_callback!(cx, check_local, l); |
dfeec247 XL |
203 | ast_visit::walk_local(cx, l); |
204 | }) | |
205 | } | |
206 | ||
207 | fn visit_block(&mut self, b: &'a ast::Block) { | |
9c376795 | 208 | lint_callback!(self, check_block, b); |
dfeec247 XL |
209 | self.check_id(b.id); |
210 | ast_visit::walk_block(self, b); | |
dfeec247 XL |
211 | } |
212 | ||
213 | fn visit_arm(&mut self, a: &'a ast::Arm) { | |
136023e0 | 214 | self.with_lint_attrs(a.id, &a.attrs, |cx| { |
9c376795 | 215 | lint_callback!(cx, check_arm, a); |
136023e0 XL |
216 | ast_visit::walk_arm(cx, a); |
217 | }) | |
dfeec247 XL |
218 | } |
219 | ||
220 | fn visit_expr_post(&mut self, e: &'a ast::Expr) { | |
5869c6ff XL |
221 | // Explicitly check for lints associated with 'closure_id', since |
222 | // it does not have a corresponding AST node | |
223 | match e.kind { | |
487cf647 FG |
224 | ast::ExprKind::Closure(box ast::Closure { |
225 | asyncness: ast::Async::Yes { closure_id, .. }, | |
226 | .. | |
227 | }) | |
5869c6ff XL |
228 | | ast::ExprKind::Async(_, closure_id, ..) => self.check_id(closure_id), |
229 | _ => {} | |
230 | } | |
dfeec247 XL |
231 | } |
232 | ||
29967ef6 | 233 | fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { |
9c376795 | 234 | lint_callback!(self, check_generic_arg, arg); |
29967ef6 XL |
235 | ast_visit::walk_generic_arg(self, arg); |
236 | } | |
237 | ||
dfeec247 | 238 | fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { |
f2b60f7d | 239 | self.with_lint_attrs(param.id, ¶m.attrs, |cx| { |
9c376795 | 240 | lint_callback!(cx, check_generic_param, param); |
f2b60f7d FG |
241 | ast_visit::walk_generic_param(cx, param); |
242 | }); | |
dfeec247 XL |
243 | } |
244 | ||
245 | fn visit_generics(&mut self, g: &'a ast::Generics) { | |
9c376795 | 246 | lint_callback!(self, check_generics, g); |
dfeec247 XL |
247 | ast_visit::walk_generics(self, g); |
248 | } | |
249 | ||
250 | fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) { | |
9c376795 | 251 | lint_callback!(self, enter_where_predicate, p); |
dfeec247 | 252 | ast_visit::walk_where_predicate(self, p); |
9c376795 | 253 | lint_callback!(self, exit_where_predicate, p); |
dfeec247 XL |
254 | } |
255 | ||
f2b60f7d | 256 | fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) { |
9c376795 | 257 | lint_callback!(self, check_poly_trait_ref, t); |
f2b60f7d | 258 | ast_visit::walk_poly_trait_ref(self, t); |
dfeec247 XL |
259 | } |
260 | ||
74b04a01 XL |
261 | fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) { |
262 | self.with_lint_attrs(item.id, &item.attrs, |cx| match ctxt { | |
263 | ast_visit::AssocCtxt::Trait => { | |
9c376795 | 264 | lint_callback!(cx, check_trait_item, item); |
74b04a01 | 265 | ast_visit::walk_assoc_item(cx, item, ctxt); |
74b04a01 XL |
266 | } |
267 | ast_visit::AssocCtxt::Impl => { | |
9c376795 | 268 | lint_callback!(cx, check_impl_item, item); |
74b04a01 | 269 | ast_visit::walk_assoc_item(cx, item, ctxt); |
74b04a01 | 270 | } |
dfeec247 XL |
271 | }); |
272 | } | |
273 | ||
923072b8 | 274 | fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { |
dfeec247 XL |
275 | self.check_id(lt.id); |
276 | } | |
277 | ||
278 | fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { | |
dfeec247 XL |
279 | self.check_id(id); |
280 | ast_visit::walk_path(self, p); | |
281 | } | |
282 | ||
f2b60f7d | 283 | fn visit_path_segment(&mut self, s: &'a ast::PathSegment) { |
04454e1e | 284 | self.check_id(s.id); |
f2b60f7d | 285 | ast_visit::walk_path_segment(self, s); |
04454e1e FG |
286 | } |
287 | ||
dfeec247 | 288 | fn visit_attribute(&mut self, attr: &'a ast::Attribute) { |
9c376795 | 289 | lint_callback!(self, check_attribute, attr); |
dfeec247 XL |
290 | } |
291 | ||
292 | fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { | |
9c376795 | 293 | lint_callback!(self, check_mac_def, mac); |
dfeec247 XL |
294 | self.check_id(id); |
295 | } | |
296 | ||
29967ef6 | 297 | fn visit_mac_call(&mut self, mac: &'a ast::MacCall) { |
9c376795 | 298 | lint_callback!(self, check_mac, mac); |
29967ef6 | 299 | ast_visit::walk_mac(self, mac); |
dfeec247 XL |
300 | } |
301 | } | |
302 | ||
9c376795 FG |
303 | // Combines multiple lint passes into a single pass, at runtime. Each |
304 | // `check_foo` method in `$methods` within this pass simply calls `check_foo` | |
305 | // once per `$pass`. Compare with `declare_combined_early_lint_pass`, which is | |
306 | // similar, but combines lint passes at compile time. | |
307 | struct RuntimeCombinedEarlyLintPass<'a> { | |
308 | passes: &'a mut [EarlyLintPassObject], | |
309 | } | |
310 | ||
311 | #[allow(rustc::lint_pass_impl_without_macro)] | |
312 | impl LintPass for RuntimeCombinedEarlyLintPass<'_> { | |
313 | fn name(&self) -> &'static str { | |
314 | panic!() | |
315 | } | |
316 | } | |
317 | ||
318 | macro_rules! impl_early_lint_pass { | |
319 | ([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => ( | |
320 | impl EarlyLintPass for RuntimeCombinedEarlyLintPass<'_> { | |
321 | $(fn $f(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) { | |
322 | for pass in self.passes.iter_mut() { | |
323 | pass.$f(context, $($param),*); | |
324 | } | |
325 | })* | |
326 | } | |
327 | ) | |
328 | } | |
329 | ||
330 | crate::early_lint_methods!(impl_early_lint_pass, []); | |
331 | ||
5099ac24 FG |
332 | /// Early lints work on different nodes - either on the crate root, or on freshly loaded modules. |
333 | /// This trait generalizes over those nodes. | |
334 | pub trait EarlyCheckNode<'a>: Copy { | |
335 | fn id(self) -> ast::NodeId; | |
336 | fn attrs<'b>(self) -> &'b [ast::Attribute] | |
337 | where | |
338 | 'a: 'b; | |
9c376795 | 339 | fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) |
5099ac24 FG |
340 | where |
341 | 'a: 'b; | |
342 | } | |
343 | ||
344 | impl<'a> EarlyCheckNode<'a> for &'a ast::Crate { | |
345 | fn id(self) -> ast::NodeId { | |
346 | ast::CRATE_NODE_ID | |
347 | } | |
348 | fn attrs<'b>(self) -> &'b [ast::Attribute] | |
349 | where | |
350 | 'a: 'b, | |
351 | { | |
352 | &self.attrs | |
353 | } | |
9c376795 | 354 | fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) |
5099ac24 FG |
355 | where |
356 | 'a: 'b, | |
357 | { | |
9c376795 | 358 | lint_callback!(cx, check_crate, self); |
5099ac24 | 359 | ast_visit::walk_crate(cx, self); |
9c376795 | 360 | lint_callback!(cx, check_crate_post, self); |
5099ac24 FG |
361 | } |
362 | } | |
363 | ||
364 | impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::Item>]) { | |
365 | fn id(self) -> ast::NodeId { | |
366 | self.0 | |
367 | } | |
368 | fn attrs<'b>(self) -> &'b [ast::Attribute] | |
369 | where | |
370 | 'a: 'b, | |
371 | { | |
372 | self.1 | |
373 | } | |
9c376795 | 374 | fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) |
5099ac24 FG |
375 | where |
376 | 'a: 'b, | |
377 | { | |
378 | walk_list!(cx, visit_attribute, self.1); | |
379 | walk_list!(cx, visit_item, self.2); | |
380 | } | |
381 | } | |
382 | ||
5099ac24 | 383 | pub fn check_ast_node<'a>( |
dfeec247 | 384 | sess: &Session, |
dfeec247 | 385 | pre_expansion: bool, |
5099ac24 FG |
386 | lint_store: &LintStore, |
387 | registered_tools: &RegisteredTools, | |
dfeec247 | 388 | lint_buffer: Option<LintBuffer>, |
487cf647 | 389 | builtin_lints: impl EarlyLintPass + 'static, |
5099ac24 | 390 | check_node: impl EarlyCheckNode<'a>, |
dfeec247 | 391 | ) { |
9c376795 FG |
392 | let context = EarlyContext::new( |
393 | sess, | |
394 | !pre_expansion, | |
395 | lint_store, | |
396 | registered_tools, | |
397 | lint_buffer.unwrap_or_default(), | |
398 | ); | |
399 | ||
400 | // Note: `passes` is often empty. In that case, it's faster to run | |
401 | // `builtin_lints` directly rather than bundling it up into the | |
402 | // `RuntimeCombinedEarlyLintPass`. | |
ba9703b0 XL |
403 | let passes = |
404 | if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; | |
9c376795 FG |
405 | if passes.is_empty() { |
406 | check_ast_node_inner(sess, check_node, context, builtin_lints); | |
407 | } else { | |
408 | let mut passes: Vec<_> = passes.iter().map(|mk_pass| (mk_pass)()).collect(); | |
409 | passes.push(Box::new(builtin_lints)); | |
410 | let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] }; | |
411 | check_ast_node_inner(sess, check_node, context, pass); | |
412 | } | |
413 | } | |
414 | ||
415 | pub fn check_ast_node_inner<'a, T: EarlyLintPass>( | |
416 | sess: &Session, | |
417 | check_node: impl EarlyCheckNode<'a>, | |
418 | context: EarlyContext<'_>, | |
419 | pass: T, | |
420 | ) { | |
421 | let mut cx = EarlyContextAndPass { context, pass }; | |
422 | ||
487cf647 | 423 | cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); |
dfeec247 XL |
424 | |
425 | // All of the buffered lints should have been emitted at this point. | |
426 | // If not, that means that we somehow buffered a lint for a node id | |
427 | // that was not lint-checked (perhaps it doesn't exist?). This is a bug. | |
487cf647 | 428 | for (id, lints) in cx.context.buffered.map { |
5869c6ff | 429 | for early_lint in lints { |
136023e0 XL |
430 | sess.delay_span_bug( |
431 | early_lint.span, | |
432 | &format!( | |
433 | "failed to process buffered lint here (dummy = {})", | |
434 | id == ast::DUMMY_NODE_ID | |
435 | ), | |
436 | ); | |
dfeec247 XL |
437 | } |
438 | } | |
439 | } |