From 218caa410aa38c29984be31a5229b9fa717560ee Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:13 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_lint/src/early.rs | 166 ++++++++++++++++++++++++++------------- 1 file changed, 111 insertions(+), 55 deletions(-) (limited to 'compiler/rustc_lint/src/early.rs') diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 52363b0be..337a19dd0 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -19,25 +19,29 @@ use crate::passes::{EarlyLintPass, EarlyLintPassObject}; use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor}; use rustc_ast::{self as ast, walk_list, HasAttrs}; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::ty::RegisteredTools; -use rustc_session::lint::{BufferedEarlyLint, LintBuffer}; +use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::Span; -macro_rules! run_early_passes { ($cx:expr, $f:ident, $($args:expr),*) => ({ - for pass in $cx.passes.iter_mut() { - pass.$f(&$cx.context, $($args),*); - } +macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ + $cx.pass.$f(&$cx.context, $($args),*); }) } -pub struct EarlyContextAndPasses<'a> { +/// Implements the AST traversal for early lint passes. `T` provides the +/// `check_*` methods. +pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { context: EarlyContext<'a>, - passes: Vec, + pass: T, } -impl<'a> EarlyContextAndPasses<'a> { - fn check_id(&mut self, id: ast::NodeId) { +impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { + // This always-inlined function is for the hot call site. + #[inline(always)] + #[allow(rustc::diagnostic_outside_of_impl)] + fn inlined_check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; self.context.lookup_with_diagnostics( @@ -50,6 +54,11 @@ impl<'a> EarlyContextAndPasses<'a> { } } + // This non-inlined function is for the cold call sites. + fn check_id(&mut self, id: ast::NodeId) { + self.inlined_check_id(id) + } + /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. @@ -61,29 +70,29 @@ impl<'a> EarlyContextAndPasses<'a> { debug!(?id); let push = self.context.builder.push(attrs, is_crate_node, None); - self.check_id(id); + self.inlined_check_id(id); debug!("early context: enter_attrs({:?})", attrs); - run_early_passes!(self, enter_lint_attrs, attrs); - f(self); + lint_callback!(self, enter_lint_attrs, attrs); + ensure_sufficient_stack(|| f(self)); debug!("early context: exit_attrs({:?})", attrs); - run_early_passes!(self, exit_lint_attrs, attrs); + lint_callback!(self, exit_lint_attrs, attrs); self.context.builder.pop(push); } } -impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { +impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> { fn visit_param(&mut self, param: &'a ast::Param) { self.with_lint_attrs(param.id, ¶m.attrs, |cx| { - run_early_passes!(cx, check_param, param); + lint_callback!(cx, check_param, param); ast_visit::walk_param(cx, param); }); } fn visit_item(&mut self, it: &'a ast::Item) { self.with_lint_attrs(it.id, &it.attrs, |cx| { - run_early_passes!(cx, check_item, it); + lint_callback!(cx, check_item, it); ast_visit::walk_item(cx, it); - run_early_passes!(cx, check_item_post, it); + lint_callback!(cx, check_item_post, it); }) } @@ -94,10 +103,10 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { } fn visit_pat(&mut self, p: &'a ast::Pat) { - run_early_passes!(self, check_pat, p); + lint_callback!(self, check_pat, p); self.check_id(p.id); ast_visit::walk_pat(self, p); - run_early_passes!(self, check_pat_post, p); + lint_callback!(self, check_pat_post, p); } fn visit_pat_field(&mut self, field: &'a ast::PatField) { @@ -113,7 +122,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { fn visit_expr(&mut self, e: &'a ast::Expr) { self.with_lint_attrs(e.id, &e.attrs, |cx| { - run_early_passes!(cx, check_expr, e); + lint_callback!(cx, check_expr, e); ast_visit::walk_expr(cx, e); }) } @@ -134,7 +143,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { // Note that statements get their attributes from // the AST struct that they wrap (e.g. an item) self.with_lint_attrs(s.id, s.attrs(), |cx| { - run_early_passes!(cx, check_stmt, s); + lint_callback!(cx, check_stmt, s); cx.check_id(s.id); }); // The visitor for the AST struct wrapped @@ -145,7 +154,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { } fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) { - run_early_passes!(self, check_fn, fk, span, id); + lint_callback!(self, check_fn, fk, span, id); self.check_id(id); ast_visit::walk_fn(self, fk); @@ -173,37 +182,37 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { fn visit_variant(&mut self, v: &'a ast::Variant) { self.with_lint_attrs(v.id, &v.attrs, |cx| { - run_early_passes!(cx, check_variant, v); + lint_callback!(cx, check_variant, v); ast_visit::walk_variant(cx, v); }) } fn visit_ty(&mut self, t: &'a ast::Ty) { - run_early_passes!(self, check_ty, t); + lint_callback!(self, check_ty, t); self.check_id(t.id); ast_visit::walk_ty(self, t); } fn visit_ident(&mut self, ident: Ident) { - run_early_passes!(self, check_ident, ident); + lint_callback!(self, check_ident, ident); } fn visit_local(&mut self, l: &'a ast::Local) { self.with_lint_attrs(l.id, &l.attrs, |cx| { - run_early_passes!(cx, check_local, l); + lint_callback!(cx, check_local, l); ast_visit::walk_local(cx, l); }) } fn visit_block(&mut self, b: &'a ast::Block) { - run_early_passes!(self, check_block, b); + lint_callback!(self, check_block, b); self.check_id(b.id); ast_visit::walk_block(self, b); } fn visit_arm(&mut self, a: &'a ast::Arm) { self.with_lint_attrs(a.id, &a.attrs, |cx| { - run_early_passes!(cx, check_arm, a); + lint_callback!(cx, check_arm, a); ast_visit::walk_arm(cx, a); }) } @@ -222,39 +231,41 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { } fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { - run_early_passes!(self, check_generic_arg, arg); + lint_callback!(self, check_generic_arg, arg); ast_visit::walk_generic_arg(self, arg); } fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { self.with_lint_attrs(param.id, ¶m.attrs, |cx| { - run_early_passes!(cx, check_generic_param, param); + lint_callback!(cx, check_generic_param, param); ast_visit::walk_generic_param(cx, param); }); } fn visit_generics(&mut self, g: &'a ast::Generics) { - run_early_passes!(self, check_generics, g); + lint_callback!(self, check_generics, g); ast_visit::walk_generics(self, g); } fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) { + lint_callback!(self, enter_where_predicate, p); ast_visit::walk_where_predicate(self, p); + lint_callback!(self, exit_where_predicate, p); } fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) { - run_early_passes!(self, check_poly_trait_ref, t); + lint_callback!(self, check_poly_trait_ref, t); ast_visit::walk_poly_trait_ref(self, t); } fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) { self.with_lint_attrs(item.id, &item.attrs, |cx| match ctxt { ast_visit::AssocCtxt::Trait => { - run_early_passes!(cx, check_trait_item, item); + lint_callback!(cx, check_trait_item, item); ast_visit::walk_assoc_item(cx, item, ctxt); } ast_visit::AssocCtxt::Impl => { - run_early_passes!(cx, check_impl_item, item); + lint_callback!(cx, check_impl_item, item); ast_visit::walk_assoc_item(cx, item, ctxt); } }); @@ -275,20 +286,49 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { } fn visit_attribute(&mut self, attr: &'a ast::Attribute) { - run_early_passes!(self, check_attribute, attr); + lint_callback!(self, check_attribute, attr); } fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { - run_early_passes!(self, check_mac_def, mac); + lint_callback!(self, check_mac_def, mac); self.check_id(id); } fn visit_mac_call(&mut self, mac: &'a ast::MacCall) { - run_early_passes!(self, check_mac, mac); + lint_callback!(self, check_mac, mac); ast_visit::walk_mac(self, mac); } } +// Combines multiple lint passes into a single pass, at runtime. Each +// `check_foo` method in `$methods` within this pass simply calls `check_foo` +// once per `$pass`. Compare with `declare_combined_early_lint_pass`, which is +// similar, but combines lint passes at compile time. +struct RuntimeCombinedEarlyLintPass<'a> { + passes: &'a mut [EarlyLintPassObject], +} + +#[allow(rustc::lint_pass_impl_without_macro)] +impl LintPass for RuntimeCombinedEarlyLintPass<'_> { + fn name(&self) -> &'static str { + panic!() + } +} + +macro_rules! impl_early_lint_pass { + ([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => ( + impl EarlyLintPass for RuntimeCombinedEarlyLintPass<'_> { + $(fn $f(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) { + for pass in self.passes.iter_mut() { + pass.$f(context, $($param),*); + } + })* + } + ) +} + +crate::early_lint_methods!(impl_early_lint_pass, []); + /// Early lints work on different nodes - either on the crate root, or on freshly loaded modules. /// This trait generalizes over those nodes. pub trait EarlyCheckNode<'a>: Copy { @@ -296,7 +336,7 @@ pub trait EarlyCheckNode<'a>: Copy { fn attrs<'b>(self) -> &'b [ast::Attribute] where 'a: 'b; - fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>) + fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) where 'a: 'b; } @@ -311,13 +351,13 @@ impl<'a> EarlyCheckNode<'a> for &'a ast::Crate { { &self.attrs } - fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>) + fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) where 'a: 'b, { - run_early_passes!(cx, check_crate, self); + lint_callback!(cx, check_crate, self); ast_visit::walk_crate(cx, self); - run_early_passes!(cx, check_crate_post, self); + lint_callback!(cx, check_crate_post, self); } } @@ -331,7 +371,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P(self, cx: &mut EarlyContextAndPasses<'b>) + fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) where 'a: 'b, { @@ -349,21 +389,37 @@ pub fn check_ast_node<'a>( builtin_lints: impl EarlyLintPass + 'static, check_node: impl EarlyCheckNode<'a>, ) { + let context = EarlyContext::new( + sess, + !pre_expansion, + lint_store, + registered_tools, + lint_buffer.unwrap_or_default(), + ); + + // Note: `passes` is often empty. In that case, it's faster to run + // `builtin_lints` directly rather than bundling it up into the + // `RuntimeCombinedEarlyLintPass`. let passes = if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; - let mut passes: Vec = passes.iter().map(|p| (p)()).collect(); - passes.push(Box::new(builtin_lints)); - - let mut cx = EarlyContextAndPasses { - context: EarlyContext::new( - sess, - !pre_expansion, - lint_store, - registered_tools, - lint_buffer.unwrap_or_default(), - ), - passes, - }; + if passes.is_empty() { + check_ast_node_inner(sess, check_node, context, builtin_lints); + } else { + let mut passes: Vec<_> = passes.iter().map(|mk_pass| (mk_pass)()).collect(); + passes.push(Box::new(builtin_lints)); + let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] }; + check_ast_node_inner(sess, check_node, context, pass); + } +} + +pub fn check_ast_node_inner<'a, T: EarlyLintPass>( + sess: &Session, + check_node: impl EarlyCheckNode<'a>, + context: EarlyContext<'_>, + pass: T, +) { + let mut cx = EarlyContextAndPass { context, pass }; + cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); // All of the buffered lints should have been emitted at this point. -- cgit v1.2.3