|
1 | 1 | use rustc_ast_pretty::pprust;
|
2 |
| -use rustc_data_structures::fx::FxIndexMap; |
3 |
| -use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; |
| 2 | +use rustc_data_structures::{fx::FxIndexMap, sync::Lrc}; |
| 3 | +use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan}; |
4 | 4 | use rustc_feature::{Features, GateIssue};
|
5 | 5 | use rustc_hir::HirId;
|
6 | 6 | use rustc_hir::intravisit::{self, Visitor};
|
@@ -31,7 +31,7 @@ use crate::errors::{
|
31 | 31 | OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
|
32 | 32 | };
|
33 | 33 | use crate::fluent_generated as fluent;
|
34 |
| -use crate::late::unerased_lint_store; |
| 34 | +use crate::late::{unerased_lint_store, name_without_tool}; |
35 | 35 | use crate::lints::{
|
36 | 36 | DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
|
37 | 37 | OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint,
|
@@ -115,6 +115,36 @@ impl LintLevelSets {
|
115 | 115 | }
|
116 | 116 | }
|
117 | 117 |
|
| 118 | +/// Walk the whole crate collecting nodes where lint levels change |
| 119 | +/// (e.g. `#[allow]` attributes), and joins that list with the warn-by-default |
| 120 | +/// (and not allowed in the crate) and CLI lints. The returned value is a tuple |
| 121 | +/// of 1. The lints that will emit (or at least, should run), and 2. |
| 122 | +/// The lints that are allowed at the crate level and will not emit. |
| 123 | +pub fn lints_that_can_emit(tcx: TyCtxt<'_>, (): ()) -> Lrc<(Vec<String>, Vec<String>)> { |
| 124 | + let mut visitor = LintLevelMinimum::new(tcx); |
| 125 | + visitor.process_opts(); |
| 126 | + tcx.hir().walk_attributes(&mut visitor); |
| 127 | + |
| 128 | + let store = unerased_lint_store(&tcx.sess); |
| 129 | + |
| 130 | + let lint_groups = store.get_lint_groups(); |
| 131 | + for group in lint_groups { |
| 132 | + let binding = group.0.to_lowercase(); |
| 133 | + let group_name = name_without_tool(&binding).to_string(); |
| 134 | + if visitor.lints_to_emit.contains(&group_name) { |
| 135 | + for lint in group.1 { |
| 136 | + visitor.lints_to_emit.push(name_without_tool(&lint.to_string()).to_string()); |
| 137 | + } |
| 138 | + } else if visitor.lints_allowed.contains(&group_name) { |
| 139 | + for lint in &group.1 { |
| 140 | + visitor.lints_allowed.push(name_without_tool(&lint.to_string()).to_string()); |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + Lrc::new((visitor.lints_to_emit, visitor.lints_allowed)) |
| 146 | +} |
| 147 | + |
118 | 148 | #[instrument(level = "trace", skip(tcx), ret)]
|
119 | 149 | fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
|
120 | 150 | let store = unerased_lint_store(tcx.sess);
|
@@ -301,6 +331,88 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
|
301 | 331 | }
|
302 | 332 | }
|
303 | 333 |
|
| 334 | +/// Visitor with the only function of visiting every item-like in a crate and |
| 335 | +/// computing the highest level that every lint gets put to. |
| 336 | +/// |
| 337 | +/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item |
| 338 | +/// uses #[warn(lint)], this visitor will set that lint level as `Warn` |
| 339 | +struct LintLevelMinimum<'tcx> { |
| 340 | + tcx: TyCtxt<'tcx>, |
| 341 | + /// The actual list of detected lints. |
| 342 | + lints_to_emit: Vec<String>, |
| 343 | + lints_allowed: Vec<String>, |
| 344 | +} |
| 345 | + |
| 346 | +impl<'tcx> LintLevelMinimum<'tcx> { |
| 347 | + pub fn new(tcx: TyCtxt<'tcx>) -> Self { |
| 348 | + Self { |
| 349 | + tcx, |
| 350 | + // That magic number is the current number of lints + some more for possible future lints |
| 351 | + lints_to_emit: Vec::with_capacity(230), |
| 352 | + lints_allowed: Vec::with_capacity(100), |
| 353 | + } |
| 354 | + } |
| 355 | + |
| 356 | + fn process_opts(&mut self) { |
| 357 | + for (lint, level) in &self.tcx.sess.opts.lint_opts { |
| 358 | + if *level == Level::Allow { |
| 359 | + self.lints_allowed.push(lint.clone()); |
| 360 | + } else { |
| 361 | + self.lints_to_emit.push(lint.to_string()); |
| 362 | + } |
| 363 | + } |
| 364 | + } |
| 365 | +} |
| 366 | + |
| 367 | +impl<'tcx> Visitor<'tcx> for LintLevelMinimum<'tcx> { |
| 368 | + type NestedFilter = nested_filter::All; |
| 369 | + |
| 370 | + fn nested_visit_map(&mut self) -> Self::Map { |
| 371 | + self.tcx.hir() |
| 372 | + } |
| 373 | + |
| 374 | + fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) { |
| 375 | + if let Some(meta) = attribute.meta() { |
| 376 | + if [sym::warn, sym::deny, sym::forbid, sym::expect] |
| 377 | + .iter() |
| 378 | + .any(|kind| meta.has_name(*kind)) |
| 379 | + { |
| 380 | + // SAFETY: Lint attributes are always a metalist inside a |
| 381 | + // metalist (even with just one lint). |
| 382 | + for meta_list in meta.meta_item_list().unwrap() { |
| 383 | + // If it's a tool lint (e.g. clippy::my_clippy_lint) |
| 384 | + if let ast::NestedMetaItem::MetaItem(meta_item) = meta_list { |
| 385 | + if meta_item.path.segments.len() == 1 { |
| 386 | + self.lints_to_emit.push( |
| 387 | + // SAFETY: Lint attributes can only have literals |
| 388 | + meta_list.ident().unwrap().name.as_str().to_string(), |
| 389 | + ); |
| 390 | + } else { |
| 391 | + self.lints_to_emit |
| 392 | + .push(meta_item.path.segments[1].ident.name.as_str().to_string()); |
| 393 | + } |
| 394 | + } |
| 395 | + } |
| 396 | + // We handle #![allow]s differently, as these remove checking rather than adding. |
| 397 | + } else if meta.has_name(sym::allow) |
| 398 | + && let ast::AttrStyle::Inner = attribute.style |
| 399 | + { |
| 400 | + for meta_list in meta.meta_item_list().unwrap() { |
| 401 | + // If it's a tool lint (e.g. clippy::my_clippy_lint) |
| 402 | + if let ast::NestedMetaItem::MetaItem(meta_item) = meta_list { |
| 403 | + if meta_item.path.segments.len() == 1 { |
| 404 | + self.lints_allowed.push(meta_list.name_or_empty().as_str().to_string()) |
| 405 | + } else { |
| 406 | + self.lints_allowed |
| 407 | + .push(meta_item.path.segments[1].ident.name.as_str().to_string()); |
| 408 | + } |
| 409 | + } |
| 410 | + } |
| 411 | + } |
| 412 | + } |
| 413 | + } |
| 414 | +} |
| 415 | + |
304 | 416 | pub struct LintLevelsBuilder<'s, P> {
|
305 | 417 | sess: &'s Session,
|
306 | 418 | features: &'s Features,
|
@@ -931,7 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
931 | 1043 | }
|
932 | 1044 |
|
933 | 1045 | pub(crate) fn provide(providers: &mut Providers) {
|
934 |
| - *providers = Providers { shallow_lint_levels_on, ..*providers }; |
| 1046 | + *providers = |
| 1047 | + Providers { shallow_lint_levels_on, lints_that_can_emit, ..*providers }; |
935 | 1048 | }
|
936 | 1049 |
|
937 | 1050 | pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
|
|
0 commit comments