1
1
use rustc_ast::attr::AttributeExt;
2
2
use rustc_ast_pretty::pprust;
3
- use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
3
+ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
4
+ use rustc_data_structures::unord::UnordSet;
4
5
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
5
6
use rustc_feature::{Features, GateIssue};
7
+ use rustc_hir::HirId;
6
8
use rustc_hir::intravisit::{self, Visitor};
7
- use rustc_hir::{CRATE_HIR_ID, HirId};
8
9
use rustc_index::IndexVec;
9
10
use rustc_middle::bug;
10
11
use rustc_middle::hir::nested_filter;
@@ -115,12 +116,11 @@ impl LintLevelSets {
115
116
}
116
117
}
117
118
118
- fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet <LintId> {
119
+ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet <LintId> {
119
120
let store = unerased_lint_store(&tcx.sess);
121
+ let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID);
120
122
121
- let map = tcx.shallow_lint_levels_on(rustc_hir::CRATE_OWNER_ID);
122
-
123
- let dont_need_to_run: FxIndexSet<LintId> = store
123
+ let mut dont_need_to_run: FxHashSet<LintId> = store
124
124
.get_lints()
125
125
.into_iter()
126
126
.filter(|lint| {
@@ -129,24 +129,31 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
129
129
lint.future_incompatible.is_some_and(|fut| fut.reason.has_future_breakage());
130
130
!has_future_breakage && !lint.eval_always
131
131
})
132
- .filter_map(|lint| {
133
- let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
134
- if matches!(lint_level.level, Level::Allow)
135
- || (matches!(lint_level.src, LintLevelSource::Default))
136
- && lint.default_level(tcx.sess.edition()) == Level::Allow
137
- {
138
- Some(LintId::of(lint))
139
- } else {
140
- None
141
- }
132
+ .filter(|lint| {
133
+ let lint_level =
134
+ root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
135
+ // Only include lints that are allowed at crate root or by default.
136
+ matches!(lint_level.level, Level::Allow)
137
+ || (matches!(lint_level.src, LintLevelSource::Default)
138
+ && lint.default_level(tcx.sess.edition()) == Level::Allow)
142
139
})
140
+ .map(|lint| LintId::of(*lint))
143
141
.collect();
144
142
145
- let mut visitor = LintLevelMaximum { tcx, dont_need_to_run };
146
- visitor.process_opts();
147
- tcx.hir_walk_attributes(&mut visitor);
143
+ for owner in tcx.hir_crate_items(()).owners() {
144
+ let map = tcx.shallow_lint_levels_on(owner);
145
+
146
+ // All lints that appear with a non-allow level must be run.
147
+ for (_, specs) in map.specs.iter() {
148
+ for (lint, level_and_source) in specs.iter() {
149
+ if !matches!(level_and_source.level, Level::Allow) {
150
+ dont_need_to_run.remove(lint);
151
+ }
152
+ }
153
+ }
154
+ }
148
155
149
- visitor. dont_need_to_run
156
+ dont_need_to_run.into()
150
157
}
151
158
152
159
#[instrument(level = "trace", skip(tcx), ret)]
@@ -340,76 +347,6 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
340
347
}
341
348
}
342
349
343
- /// Visitor with the only function of visiting every item-like in a crate and
344
- /// computing the highest level that every lint gets put to.
345
- ///
346
- /// E.g., if a crate has a global #![allow(lint)] attribute, but a single item
347
- /// uses #[warn(lint)], this visitor will set that lint level as `Warn`
348
- struct LintLevelMaximum<'tcx> {
349
- tcx: TyCtxt<'tcx>,
350
- /// The actual list of detected lints.
351
- dont_need_to_run: FxIndexSet<LintId>,
352
- }
353
-
354
- impl<'tcx> LintLevelMaximum<'tcx> {
355
- fn process_opts(&mut self) {
356
- let store = unerased_lint_store(self.tcx.sess);
357
- for (lint_group, level) in &self.tcx.sess.opts.lint_opts {
358
- if *level != Level::Allow {
359
- let Ok(lints) = store.find_lints(lint_group) else {
360
- return;
361
- };
362
- for lint in lints {
363
- self.dont_need_to_run.swap_remove(&lint);
364
- }
365
- }
366
- }
367
- }
368
- }
369
-
370
- impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
371
- type NestedFilter = nested_filter::All;
372
-
373
- fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
374
- self.tcx
375
- }
376
-
377
- /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s,
378
- /// but that is handled with more care
379
- fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) {
380
- if matches!(
381
- Level::from_attr(attribute),
382
- Some((Level::Warn | Level::Deny | Level::Forbid | Level::Expect | Level::ForceWarn, _))
383
- ) {
384
- let store = unerased_lint_store(self.tcx.sess);
385
- // Lint attributes are always a metalist inside a
386
- // metalist (even with just one lint).
387
- let Some(meta_item_list) = attribute.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
-
413
350
pub struct LintLevelsBuilder<'s, P> {
414
351
sess: &'s Session,
415
352
features: &'s Features,
0 commit comments