|
1 | 1 | use rustc_ast_pretty::pprust;
|
2 |
| -use rustc_data_structures::fx::FxIndexMap; |
| 2 | +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; |
3 | 3 | use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
|
4 | 4 | use rustc_feature::{Features, GateIssue};
|
5 |
| -use rustc_hir::HirId; |
6 | 5 | use rustc_hir::intravisit::{self, Visitor};
|
| 6 | +use rustc_hir::{CRATE_HIR_ID, HirId}; |
7 | 7 | use rustc_index::IndexVec;
|
8 | 8 | use rustc_middle::bug;
|
9 | 9 | use rustc_middle::hir::nested_filter;
|
@@ -115,6 +115,38 @@ impl LintLevelSets {
|
115 | 115 | }
|
116 | 116 | }
|
117 | 117 |
|
| 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 | + |
118 | 150 | #[instrument(level = "trace", skip(tcx), ret)]
|
119 | 151 | fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
|
120 | 152 | let store = unerased_lint_store(tcx.sess);
|
@@ -301,6 +333,83 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
|
301 | 333 | }
|
302 | 334 | }
|
303 | 335 |
|
| 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 | + |
304 | 413 | pub struct LintLevelsBuilder<'s, P> {
|
305 | 414 | sess: &'s Session,
|
306 | 415 | features: &'s Features,
|
@@ -934,7 +1043,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
934 | 1043 | }
|
935 | 1044 |
|
936 | 1045 | 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 }; |
938 | 1047 | }
|
939 | 1048 |
|
940 | 1049 | pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
|
|
0 commit comments