Skip to content

Commit 4d88de2

Browse files
committed
Auto merge of rust-lang#125116 - blyxyas:ignore-allowed-lints-final, r=cjgillot
(Big performance change) Do not run lints that cannot emit Before this change, adding a lint was a difficult matter because it always had some overhead involved. This was because all lints would run, no matter their default level, or if the user had `#![allow]`ed them. This PR changes that. This change would improve both the Rust lint infrastructure and Clippy, but Clippy will see the most benefit, as it has about 900 registered lints (and growing!) So yeah, with this little patch we filter all lints pre-linting, and remove any lint that is either: - Manually `#![allow]`ed in the whole crate, - Allowed in the command line, or - Not manually enabled with `#[warn]` or similar, and its default level is `Allow` As some lints **need** to run, this PR also adds **loadbearing lints**. On a lint declaration, you can use the ``@eval_always` = true` marker to label it as loadbearing. A loadbearing lint will never be filtered (it will always run) Fixes rust-lang#106983
2 parents 9260be3 + 1dcfa27 commit 4d88de2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+291
-56
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4029,6 +4029,7 @@ dependencies = [
40294029
"rustc_hir",
40304030
"rustc_hir_pretty",
40314031
"rustc_index",
4032+
"rustc_lint_defs",
40324033
"rustc_macros",
40334034
"rustc_query_system",
40344035
"rustc_serialize",

compiler/rustc_ast/src/attr/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ impl AttrItem {
223223
self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
224224
}
225225

226-
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
226+
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
227227
match &self.args {
228228
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
229229
MetaItemKind::list_from_tokens(args.tokens.clone())

compiler/rustc_lint/src/builtin.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ use crate::{
7373
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
7474
fluent_generated as fluent,
7575
};
76-
7776
declare_lint! {
7877
/// The `while_true` lint detects `while true { }`.
7978
///
@@ -241,7 +240,8 @@ declare_lint! {
241240
/// behavior.
242241
UNSAFE_CODE,
243242
Allow,
244-
"usage of `unsafe` code and other potentially unsound constructs"
243+
"usage of `unsafe` code and other potentially unsound constructs",
244+
@eval_always = true
245245
}
246246

247247
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
@@ -389,6 +389,7 @@ declare_lint! {
389389
report_in_external_macro
390390
}
391391

392+
#[derive(Default)]
392393
pub struct MissingDoc;
393394

394395
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
@@ -819,8 +820,8 @@ pub struct DeprecatedAttr {
819820

820821
impl_lint_pass!(DeprecatedAttr => []);
821822

822-
impl DeprecatedAttr {
823-
pub fn new() -> DeprecatedAttr {
823+
impl Default for DeprecatedAttr {
824+
fn default() -> Self {
824825
DeprecatedAttr { depr_attrs: deprecated_attributes() }
825826
}
826827
}

compiler/rustc_lint/src/early.rs

+3
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ impl LintPass for RuntimeCombinedEarlyLintPass<'_> {
309309
fn name(&self) -> &'static str {
310310
panic!()
311311
}
312+
fn get_lints(&self) -> crate::LintVec {
313+
panic!()
314+
}
312315
}
313316

314317
macro_rules! impl_early_lint_pass {

compiler/rustc_lint/src/internal.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,8 @@ declare_tool_lint! {
429429
pub rustc::UNTRANSLATABLE_DIAGNOSTIC,
430430
Deny,
431431
"prevent creation of diagnostics which cannot be translated",
432-
report_in_external_macro: true
432+
report_in_external_macro: true,
433+
@eval_always = true
433434
}
434435

435436
declare_tool_lint! {
@@ -442,7 +443,8 @@ declare_tool_lint! {
442443
pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL,
443444
Deny,
444445
"prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls",
445-
report_in_external_macro: true
446+
report_in_external_macro: true,
447+
@eval_always = true
446448
}
447449

448450
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);

compiler/rustc_lint/src/late.rs

+30-8
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ use rustc_middle::hir::nested_filter;
2626
use rustc_middle::ty::{self, TyCtxt};
2727
use rustc_session::Session;
2828
use rustc_session::lint::LintPass;
29+
use rustc_session::lint::builtin::HardwiredLints;
2930
use rustc_span::Span;
3031
use tracing::debug;
3132

3233
use crate::passes::LateLintPassObject;
33-
use crate::{LateContext, LateLintPass, LintStore};
34+
use crate::{LateContext, LateLintPass, LintId, LintStore};
3435

3536
/// Extract the [`LintStore`] from [`Session`].
3637
///
@@ -326,6 +327,9 @@ impl LintPass for RuntimeCombinedLateLintPass<'_, '_> {
326327
fn name(&self) -> &'static str {
327328
panic!()
328329
}
330+
fn get_lints(&self) -> crate::LintVec {
331+
panic!()
332+
}
329333
}
330334

331335
macro_rules! impl_late_lint_pass {
@@ -361,13 +365,20 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
361365
// Note: `passes` is often empty. In that case, it's faster to run
362366
// `builtin_lints` directly rather than bundling it up into the
363367
// `RuntimeCombinedLateLintPass`.
364-
let late_module_passes = &unerased_lint_store(tcx.sess).late_module_passes;
365-
if late_module_passes.is_empty() {
368+
let store = unerased_lint_store(tcx.sess);
369+
370+
if store.late_module_passes.is_empty() {
366371
late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
367372
} else {
368-
let mut passes: Vec<_> = late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
369-
passes.push(Box::new(builtin_lints));
370-
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
373+
let builtin_lints = Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>;
374+
let mut binding = store
375+
.late_module_passes
376+
.iter()
377+
.map(|mk_pass| (mk_pass)(tcx))
378+
.chain(std::iter::once(builtin_lints))
379+
.collect::<Vec<_>>();
380+
381+
let pass = RuntimeCombinedLateLintPass { passes: binding.as_mut_slice() };
371382
late_lint_mod_inner(tcx, module_def_id, context, pass);
372383
}
373384
}
@@ -398,7 +409,7 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
398409

399410
fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
400411
// Note: `passes` is often empty.
401-
let mut passes: Vec<_> =
412+
let passes: Vec<_> =
402413
unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
403414

404415
if passes.is_empty() {
@@ -416,7 +427,18 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
416427
only_module: false,
417428
};
418429

419-
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
430+
let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(());
431+
432+
let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes
433+
.into_iter()
434+
.filter(|pass| {
435+
let lints = (**pass).get_lints();
436+
!lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint)))
437+
})
438+
.collect();
439+
440+
filtered_passes.push(Box::new(HardwiredLints));
441+
let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] };
420442
late_lint_crate_inner(tcx, context, pass);
421443
}
422444

compiler/rustc_lint/src/levels.rs

+112-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use rustc_ast_pretty::pprust;
2-
use rustc_data_structures::fx::FxIndexMap;
2+
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
33
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
44
use rustc_feature::{Features, GateIssue};
5-
use rustc_hir::HirId;
65
use rustc_hir::intravisit::{self, Visitor};
6+
use rustc_hir::{CRATE_HIR_ID, HirId};
77
use rustc_index::IndexVec;
88
use rustc_middle::bug;
99
use rustc_middle::hir::nested_filter;
@@ -115,6 +115,38 @@ impl LintLevelSets {
115115
}
116116
}
117117

118+
fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
119+
let store = unerased_lint_store(&tcx.sess);
120+
121+
let map = tcx.shallow_lint_levels_on(rustc_hir::CRATE_OWNER_ID);
122+
123+
let dont_need_to_run: FxIndexSet<LintId> = store
124+
.get_lints()
125+
.into_iter()
126+
.filter_map(|lint| {
127+
if !lint.eval_always {
128+
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
129+
if matches!(lint_level, (Level::Allow, ..))
130+
|| (matches!(lint_level, (.., LintLevelSource::Default)))
131+
&& lint.default_level(tcx.sess.edition()) == Level::Allow
132+
{
133+
Some(LintId::of(lint))
134+
} else {
135+
None
136+
}
137+
} else {
138+
None
139+
}
140+
})
141+
.collect();
142+
143+
let mut visitor = LintLevelMaximum { tcx, dont_need_to_run };
144+
visitor.process_opts();
145+
tcx.hir().walk_attributes(&mut visitor);
146+
147+
visitor.dont_need_to_run
148+
}
149+
118150
#[instrument(level = "trace", skip(tcx), ret)]
119151
fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
120152
let store = unerased_lint_store(tcx.sess);
@@ -301,6 +333,83 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
301333
}
302334
}
303335

336+
/// Visitor with the only function of visiting every item-like in a crate and
337+
/// computing the highest level that every lint gets put to.
338+
///
339+
/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item
340+
/// uses #[warn(lint)], this visitor will set that lint level as `Warn`
341+
struct LintLevelMaximum<'tcx> {
342+
tcx: TyCtxt<'tcx>,
343+
/// The actual list of detected lints.
344+
dont_need_to_run: FxIndexSet<LintId>,
345+
}
346+
347+
impl<'tcx> LintLevelMaximum<'tcx> {
348+
fn process_opts(&mut self) {
349+
let store = unerased_lint_store(self.tcx.sess);
350+
for (lint_group, level) in &self.tcx.sess.opts.lint_opts {
351+
if *level != Level::Allow {
352+
let Ok(lints) = store.find_lints(lint_group) else {
353+
return;
354+
};
355+
for lint in lints {
356+
self.dont_need_to_run.swap_remove(&lint);
357+
}
358+
}
359+
}
360+
}
361+
}
362+
363+
impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
364+
type NestedFilter = nested_filter::All;
365+
366+
fn nested_visit_map(&mut self) -> Self::Map {
367+
self.tcx.hir()
368+
}
369+
370+
/// FIXME(blyxyas): In a future revision, we should also graph #![allow]s,
371+
/// but that is handled with more care
372+
fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) {
373+
if matches!(
374+
Level::from_attr(attribute),
375+
Some(
376+
Level::Warn
377+
| Level::Deny
378+
| Level::Forbid
379+
| Level::Expect(..)
380+
| Level::ForceWarn(..),
381+
)
382+
) {
383+
let store = unerased_lint_store(self.tcx.sess);
384+
let Some(meta) = attribute.meta() else { return };
385+
// Lint attributes are always a metalist inside a
386+
// metalist (even with just one lint).
387+
let Some(meta_item_list) = meta.meta_item_list() else { return };
388+
389+
for meta_list in meta_item_list {
390+
// Convert Path to String
391+
let Some(meta_item) = meta_list.meta_item() else { return };
392+
let ident: &str = &meta_item
393+
.path
394+
.segments
395+
.iter()
396+
.map(|segment| segment.ident.as_str())
397+
.collect::<Vec<&str>>()
398+
.join("::");
399+
let Ok(lints) = store.find_lints(
400+
// Lint attributes can only have literals
401+
ident,
402+
) else {
403+
return;
404+
};
405+
for lint in lints {
406+
self.dont_need_to_run.swap_remove(&lint);
407+
}
408+
}
409+
}
410+
}
411+
}
412+
304413
pub struct LintLevelsBuilder<'s, P> {
305414
sess: &'s Session,
306415
features: &'s Features,
@@ -934,7 +1043,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
9341043
}
9351044

9361045
pub(crate) fn provide(providers: &mut Providers) {
937-
*providers = Providers { shallow_lint_levels_on, ..*providers };
1046+
*providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers };
9381047
}
9391048

9401049
pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {

compiler/rustc_lint/src/lib.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,15 @@ early_lint_methods!(
170170
[
171171
pub BuiltinCombinedEarlyLintPass,
172172
[
173-
UnusedParens: UnusedParens::new(),
173+
UnusedParens: UnusedParens::default(),
174174
UnusedBraces: UnusedBraces,
175175
UnusedImportBraces: UnusedImportBraces,
176176
UnsafeCode: UnsafeCode,
177177
SpecialModuleName: SpecialModuleName,
178178
AnonymousParameters: AnonymousParameters,
179179
EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
180180
NonCamelCaseTypes: NonCamelCaseTypes,
181-
DeprecatedAttr: DeprecatedAttr::new(),
181+
DeprecatedAttr: DeprecatedAttr::default(),
182182
WhileTrue: WhileTrue,
183183
NonAsciiIdents: NonAsciiIdents,
184184
HiddenUnicodeCodepoints: HiddenUnicodeCodepoints,
@@ -199,7 +199,6 @@ late_lint_methods!(
199199
ForLoopsOverFallibles: ForLoopsOverFallibles,
200200
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
201201
DropForgetUseless: DropForgetUseless,
202-
HardwiredLints: HardwiredLints,
203202
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
204203
ImproperCTypesDefinitions: ImproperCTypesDefinitions,
205204
InvalidFromUtf8: InvalidFromUtf8,
@@ -280,6 +279,7 @@ fn register_builtins(store: &mut LintStore) {
280279
store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints());
281280
store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints());
282281
store.register_lints(&foreign_modules::get_lints());
282+
store.register_lints(&HardwiredLints::lint_vec());
283283

284284
add_lint_group!(
285285
"nonstandard_style",
@@ -602,25 +602,25 @@ fn register_builtins(store: &mut LintStore) {
602602
}
603603

604604
fn register_internals(store: &mut LintStore) {
605-
store.register_lints(&LintPassImpl::get_lints());
605+
store.register_lints(&LintPassImpl::lint_vec());
606606
store.register_early_pass(|| Box::new(LintPassImpl));
607-
store.register_lints(&DefaultHashTypes::get_lints());
607+
store.register_lints(&DefaultHashTypes::lint_vec());
608608
store.register_late_mod_pass(|_| Box::new(DefaultHashTypes));
609-
store.register_lints(&QueryStability::get_lints());
609+
store.register_lints(&QueryStability::lint_vec());
610610
store.register_late_mod_pass(|_| Box::new(QueryStability));
611-
store.register_lints(&ExistingDocKeyword::get_lints());
611+
store.register_lints(&ExistingDocKeyword::lint_vec());
612612
store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword));
613-
store.register_lints(&TyTyKind::get_lints());
613+
store.register_lints(&TyTyKind::lint_vec());
614614
store.register_late_mod_pass(|_| Box::new(TyTyKind));
615-
store.register_lints(&TypeIr::get_lints());
615+
store.register_lints(&TypeIr::lint_vec());
616616
store.register_late_mod_pass(|_| Box::new(TypeIr));
617-
store.register_lints(&Diagnostics::get_lints());
617+
store.register_lints(&Diagnostics::lint_vec());
618618
store.register_late_mod_pass(|_| Box::new(Diagnostics));
619-
store.register_lints(&BadOptAccess::get_lints());
619+
store.register_lints(&BadOptAccess::lint_vec());
620620
store.register_late_mod_pass(|_| Box::new(BadOptAccess));
621-
store.register_lints(&PassByValue::get_lints());
621+
store.register_lints(&PassByValue::lint_vec());
622622
store.register_late_mod_pass(|_| Box::new(PassByValue));
623-
store.register_lints(&SpanUseEqCtxt::get_lints());
623+
store.register_lints(&SpanUseEqCtxt::lint_vec());
624624
store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt));
625625
// FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
626626
// `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and

0 commit comments

Comments
 (0)