-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(Big performance change) Do not run lints that cannot emit #125116
base: master
Are you sure you want to change the base?
Changes from all commits
414f7e5
cb1688c
5ae3622
11e09a3
04bac39
dd90950
768e542
01f6459
bc41a6b
e1af312
6ecd060
5f8fc79
b5ac9b5
bdf21a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
//! If you define a new `LateLintPass`, you will also need to add it to the | ||
//! `late_lint_methods!` invocation in `lib.rs`. | ||
|
||
use std::default::Default; | ||
use std::fmt::Write; | ||
|
||
use ast::token::TokenKind; | ||
|
@@ -73,6 +74,10 @@ use crate::{ | |
fluent_generated as fluent, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, | ||
LintContext, | ||
}; | ||
// use std::fmt::Write; | ||
|
||
// hardwired lints from rustc_lint_defs | ||
// pub use rustc_session::lint::builtin::*; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commented-out code. |
||
|
||
declare_lint! { | ||
/// The `while_true` lint detects `while true { }`. | ||
|
@@ -242,7 +247,8 @@ declare_lint! { | |
/// behavior. | ||
UNSAFE_CODE, | ||
Allow, | ||
"usage of `unsafe` code and other potentially unsound constructs" | ||
"usage of `unsafe` code and other potentially unsound constructs", | ||
[eval_always: true] | ||
} | ||
|
||
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); | ||
|
@@ -390,6 +396,7 @@ declare_lint! { | |
report_in_external_macro | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct MissingDoc; | ||
|
||
impl_lint_pass!(MissingDoc => [MISSING_DOCS]); | ||
|
@@ -830,8 +837,8 @@ pub struct DeprecatedAttr { | |
|
||
impl_lint_pass!(DeprecatedAttr => []); | ||
|
||
impl DeprecatedAttr { | ||
pub fn new() -> DeprecatedAttr { | ||
impl Default for DeprecatedAttr { | ||
fn default() -> Self { | ||
DeprecatedAttr { depr_attrs: deprecated_attributes() } | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -439,7 +439,8 @@ declare_tool_lint! { | |
pub rustc::UNTRANSLATABLE_DIAGNOSTIC, | ||
Deny, | ||
"prevent creation of diagnostics which cannot be translated", | ||
report_in_external_macro: true | ||
report_in_external_macro: true, | ||
[eval_always: true] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could the syntax be closer to other modifiers? |
||
} | ||
|
||
declare_tool_lint! { | ||
|
@@ -452,7 +453,8 @@ declare_tool_lint! { | |
pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL, | ||
Deny, | ||
"prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls", | ||
report_in_external_macro: true | ||
report_in_external_macro: true, | ||
[eval_always: true] | ||
} | ||
|
||
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]); | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -24,13 +24,14 @@ use rustc_hir::def_id::{LocalDefId, LocalModDefId}; | |||||
use rustc_hir::{intravisit as hir_visit, HirId}; | ||||||
use rustc_middle::hir::nested_filter; | ||||||
use rustc_middle::ty::{self, TyCtxt}; | ||||||
use rustc_session::lint::builtin::HardwiredLints; | ||||||
use rustc_session::lint::LintPass; | ||||||
use rustc_session::Session; | ||||||
use rustc_span::Span; | ||||||
use tracing::debug; | ||||||
|
||||||
use crate::passes::LateLintPassObject; | ||||||
use crate::{LateContext, LateLintPass, LintStore}; | ||||||
use crate::{LateContext, LateLintPass, LintId, LintStore}; | ||||||
|
||||||
/// Extract the [`LintStore`] from [`Session`]. | ||||||
/// | ||||||
|
@@ -327,6 +328,9 @@ impl LintPass for RuntimeCombinedLateLintPass<'_, '_> { | |||||
fn name(&self) -> &'static str { | ||||||
panic!() | ||||||
} | ||||||
fn get_lints(&self) -> crate::LintVec { | ||||||
panic!() | ||||||
} | ||||||
} | ||||||
|
||||||
macro_rules! impl_late_lint_pass { | ||||||
|
@@ -362,13 +366,20 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( | |||||
// Note: `passes` is often empty. In that case, it's faster to run | ||||||
// `builtin_lints` directly rather than bundling it up into the | ||||||
// `RuntimeCombinedLateLintPass`. | ||||||
let late_module_passes = &unerased_lint_store(tcx.sess).late_module_passes; | ||||||
if late_module_passes.is_empty() { | ||||||
let store = unerased_lint_store(tcx.sess); | ||||||
|
||||||
if store.late_module_passes.is_empty() { | ||||||
late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); | ||||||
} else { | ||||||
let mut passes: Vec<_> = late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); | ||||||
passes.push(Box::new(builtin_lints)); | ||||||
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; | ||||||
let chained_box = Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
? |
||||||
let mut binding = store | ||||||
.late_module_passes | ||||||
.iter() | ||||||
.map(|mk_pass| (mk_pass)(tcx)) | ||||||
.chain(std::iter::once(chained_box)) | ||||||
.collect::<Vec<_>>(); | ||||||
|
||||||
let pass = RuntimeCombinedLateLintPass { passes: binding.as_mut_slice() }; | ||||||
late_lint_mod_inner(tcx, module_def_id, context, pass); | ||||||
} | ||||||
} | ||||||
|
@@ -399,7 +410,7 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( | |||||
|
||||||
fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { | ||||||
// Note: `passes` is often empty. | ||||||
let mut passes: Vec<_> = | ||||||
let passes: Vec<_> = | ||||||
unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); | ||||||
|
||||||
if passes.is_empty() { | ||||||
|
@@ -417,7 +428,29 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { | |||||
only_module: false, | ||||||
}; | ||||||
|
||||||
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; | ||||||
let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(()); | ||||||
|
||||||
let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes | ||||||
.into_iter() | ||||||
.filter(|pass| { | ||||||
let lints = LintPass::get_lints(pass); | ||||||
!lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint))) | ||||||
}) | ||||||
.collect(); | ||||||
|
||||||
filtered_passes.push(Box::new(HardwiredLints)); | ||||||
|
||||||
// let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes | ||||||
// .into_iter() | ||||||
// .filter(|pass| { | ||||||
// let lints = LintPass::get_lints(pass); | ||||||
// lints.iter() | ||||||
// .any(|lint| | ||||||
// !lints_that_dont_need_to_run.contains(&LintId::of(lint))) | ||||||
// }).collect(); | ||||||
// | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commented-out code. |
||||||
|
||||||
let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] }; | ||||||
late_lint_crate_inner(tcx, context, pass); | ||||||
} | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
use rustc_ast_pretty::pprust; | ||
use rustc_data_structures::fx::FxIndexMap; | ||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; | ||
use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; | ||
use rustc_feature::{Features, GateIssue}; | ||
use rustc_hir::intravisit::{self, Visitor}; | ||
|
@@ -115,6 +115,28 @@ impl LintLevelSets { | |
} | ||
} | ||
|
||
fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> { | ||
let store = unerased_lint_store(&tcx.sess); | ||
|
||
let dont_need_to_run: FxIndexSet<LintId> = store | ||
.get_lints() | ||
.into_iter() | ||
.filter_map(|lint| { | ||
if !lint.eval_always && lint.default_level(tcx.sess.edition()) == Level::Allow { | ||
Some(LintId::of(lint)) | ||
} else { | ||
None | ||
} | ||
}) | ||
.collect(); | ||
|
||
let mut visitor = LintLevelMaximum { tcx, dont_need_to_run }; | ||
visitor.process_opts(); | ||
tcx.hir().walk_attributes(&mut visitor); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be implemented by calling |
||
|
||
visitor.dont_need_to_run | ||
} | ||
|
||
#[instrument(level = "trace", skip(tcx), ret)] | ||
fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap { | ||
let store = unerased_lint_store(tcx.sess); | ||
|
@@ -303,6 +325,83 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { | |
} | ||
} | ||
|
||
/// Visitor with the only function of visiting every item-like in a crate and | ||
/// computing the highest level that every lint gets put to. | ||
/// | ||
/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item | ||
/// uses #[warn(lint)], this visitor will set that lint level as `Warn` | ||
struct LintLevelMaximum<'tcx> { | ||
tcx: TyCtxt<'tcx>, | ||
/// The actual list of detected lints. | ||
dont_need_to_run: FxIndexSet<LintId>, | ||
} | ||
|
||
impl<'tcx> LintLevelMaximum<'tcx> { | ||
fn process_opts(&mut self) { | ||
let store = unerased_lint_store(self.tcx.sess); | ||
for (lint_group, level) in &self.tcx.sess.opts.lint_opts { | ||
if *level != Level::Allow { | ||
let Ok(lints) = store.find_lints(lint_group) else { | ||
return; | ||
}; | ||
for lint in lints { | ||
self.dont_need_to_run.swap_remove(&lint); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { | ||
type NestedFilter = nested_filter::All; | ||
|
||
fn nested_visit_map(&mut self) -> Self::Map { | ||
self.tcx.hir() | ||
} | ||
|
||
/// FIXME(blyxyas): In a future revision, we should also graph #![allow]s, | ||
/// but that is handled with more care | ||
fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) { | ||
if matches!( | ||
Level::from_attr(attribute), | ||
Some( | ||
Level::Warn | ||
| Level::Deny | ||
| Level::Forbid | ||
| Level::Expect(..) | ||
| Level::ForceWarn(..), | ||
) | ||
) { | ||
let store = unerased_lint_store(self.tcx.sess); | ||
let Some(meta) = attribute.meta() else { return }; | ||
// SAFETY: Lint attributes are always a metalist inside a | ||
// metalist (even with just one lint). | ||
let Some(meta_item_list) = meta.meta_item_list() else { return }; | ||
|
||
for meta_list in meta_item_list { | ||
// Convert Path to String | ||
let Some(meta_item) = meta_list.meta_item() else { return }; | ||
let ident: &str = &meta_item | ||
.path | ||
.segments | ||
.iter() | ||
.map(|segment| segment.ident.as_str()) | ||
.collect::<Vec<&str>>() | ||
.join("::"); | ||
let Ok(lints) = store.find_lints( | ||
// SAFETY: Lint attributes can only have literals | ||
ident, | ||
) else { | ||
return; | ||
}; | ||
for lint in lints { | ||
self.dont_need_to_run.swap_remove(&lint); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub struct LintLevelsBuilder<'s, P> { | ||
sess: &'s Session, | ||
features: &'s Features, | ||
|
@@ -933,7 +1032,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { | |
} | ||
|
||
pub(crate) fn provide(providers: &mut Providers) { | ||
*providers = Providers { shallow_lint_levels_on, ..*providers }; | ||
*providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers }; | ||
} | ||
|
||
pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) { | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -31,6 +31,7 @@ | |||||
#![doc(rust_logo)] | ||||||
#![feature(array_windows)] | ||||||
#![feature(assert_matches)] | ||||||
#![feature(box_into_inner)] | ||||||
#![feature(box_patterns)] | ||||||
#![feature(control_flow_enum)] | ||||||
#![feature(extract_if)] | ||||||
|
@@ -165,15 +166,15 @@ early_lint_methods!( | |||||
[ | ||||||
pub BuiltinCombinedEarlyLintPass, | ||||||
[ | ||||||
UnusedParens: UnusedParens::new(), | ||||||
UnusedParens: UnusedParens::default(), | ||||||
UnusedBraces: UnusedBraces, | ||||||
UnusedImportBraces: UnusedImportBraces, | ||||||
UnsafeCode: UnsafeCode, | ||||||
SpecialModuleName: SpecialModuleName, | ||||||
AnonymousParameters: AnonymousParameters, | ||||||
EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), | ||||||
NonCamelCaseTypes: NonCamelCaseTypes, | ||||||
DeprecatedAttr: DeprecatedAttr::new(), | ||||||
DeprecatedAttr: DeprecatedAttr::default(), | ||||||
WhileTrue: WhileTrue, | ||||||
NonAsciiIdents: NonAsciiIdents, | ||||||
HiddenUnicodeCodepoints: HiddenUnicodeCodepoints, | ||||||
|
@@ -194,7 +195,6 @@ late_lint_methods!( | |||||
ForLoopsOverFallibles: ForLoopsOverFallibles, | ||||||
DerefIntoDynSupertrait: DerefIntoDynSupertrait, | ||||||
DropForgetUseless: DropForgetUseless, | ||||||
HardwiredLints: HardwiredLints, | ||||||
ImproperCTypesDeclarations: ImproperCTypesDeclarations, | ||||||
ImproperCTypesDefinitions: ImproperCTypesDefinitions, | ||||||
InvalidFromUtf8: InvalidFromUtf8, | ||||||
|
@@ -272,6 +272,7 @@ fn register_builtins(store: &mut LintStore) { | |||||
store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); | ||||||
store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); | ||||||
store.register_lints(&foreign_modules::get_lints()); | ||||||
store.register_lints(&HardwiredLints::default().get_lints()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
? |
||||||
|
||||||
add_lint_group!( | ||||||
"nonstandard_style", | ||||||
|
@@ -579,25 +580,25 @@ fn register_builtins(store: &mut LintStore) { | |||||
} | ||||||
|
||||||
fn register_internals(store: &mut LintStore) { | ||||||
store.register_lints(&LintPassImpl::get_lints()); | ||||||
store.register_lints(&LintPassImpl::default().get_lints()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
and everywhere below. |
||||||
store.register_early_pass(|| Box::new(LintPassImpl)); | ||||||
store.register_lints(&DefaultHashTypes::get_lints()); | ||||||
store.register_lints(&DefaultHashTypes::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(DefaultHashTypes)); | ||||||
store.register_lints(&QueryStability::get_lints()); | ||||||
store.register_lints(&QueryStability::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(QueryStability)); | ||||||
store.register_lints(&ExistingDocKeyword::get_lints()); | ||||||
store.register_lints(&ExistingDocKeyword::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword)); | ||||||
store.register_lints(&TyTyKind::get_lints()); | ||||||
store.register_lints(&TyTyKind::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(TyTyKind)); | ||||||
store.register_lints(&TypeIr::get_lints()); | ||||||
store.register_lints(&TypeIr::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(TypeIr)); | ||||||
store.register_lints(&Diagnostics::get_lints()); | ||||||
store.register_lints(&Diagnostics::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(Diagnostics)); | ||||||
store.register_lints(&BadOptAccess::get_lints()); | ||||||
store.register_lints(&BadOptAccess::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(BadOptAccess)); | ||||||
store.register_lints(&PassByValue::get_lints()); | ||||||
store.register_lints(&PassByValue::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(PassByValue)); | ||||||
store.register_lints(&SpanUseEqCtxt::get_lints()); | ||||||
store.register_lints(&SpanUseEqCtxt::default().get_lints()); | ||||||
store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt)); | ||||||
// FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and | ||||||
// `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this in prelude?