diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 3a3aeb882164..4d62c146793a 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -9,6 +9,8 @@ clippy_config = { path = "../clippy_config" } arrayvec = { version = "0.7", default-features = false } itertools = "0.12" rustc-semver = "1.1" +rustc-hash = "1.1" +lazy_static = "1.4" [features] deny-warnings = ["clippy_config/deny-warnings"] diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index d2200bcf7103..15b7f6b61dbe 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,15 +1,57 @@ +use lazy_static::lazy_static; use rustc_ast::{ast, attr}; use rustc_errors::Applicability; +use rustc_hash::FxHashSet; use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; use rustc_span::{sym, Span}; use std::str::FromStr; +use std::sync::RwLock; + +use std::io::{self, Write}; use crate::source::snippet_opt; use crate::tokenize_with_text; +pub struct EmissionState { + emitted: RwLock>, +} + +impl EmissionState { + pub fn new() -> Self { + Self { + emitted: RwLock::new(FxHashSet::default()), + } + } + + pub fn has_emitted(&self, attr_name: &str) -> bool { + let emitted = self.emitted.read().unwrap(); + emitted.contains(attr_name) + } + + pub fn set_emitted(&self, attr_name: &str) { + let mut emitted = self.emitted.write().unwrap(); + emitted.insert(attr_name.to_string()); + } + + pub fn reset(&self) { + let mut emitted = self.emitted.write().unwrap(); + emitted.clear(); + } +} + +impl Default for EmissionState { + fn default() -> Self { + Self::new() + } +} + +lazy_static! { + pub static ref GLOBAL_EMISSION_STATE: EmissionState = EmissionState::new(); +} + /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { /// Attribute is deprecated @@ -69,8 +111,12 @@ pub fn get_attr<'a>( } else { return false; }; + let attr_segments = &attr.path.segments; + if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy { + let attr_name = attr_segments[1].ident.name.as_str().to_string(); + BUILTIN_ATTRIBUTES .iter() .find_map(|&(builtin_name, ref deprecation_status)| { @@ -87,28 +133,39 @@ pub fn get_attr<'a>( false }, |deprecation_status| { - let mut diag = sess - .dcx() - .struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute"); - match *deprecation_status { - DeprecationStatus::Deprecated => { - diag.emit(); - false - }, - DeprecationStatus::Replaced(new_name) => { - diag.span_suggestion( - attr_segments[1].ident.span, - "consider using", - new_name, - Applicability::MachineApplicable, - ); - diag.emit(); - false - }, - DeprecationStatus::None => { - diag.cancel(); - attr_segments[1].ident.name.as_str() == name - }, + if GLOBAL_EMISSION_STATE.has_emitted(&attr_name) { + false + } else { + let mut diag = sess + .dcx() + .struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute"); + + match *deprecation_status { + DeprecationStatus::Deprecated => { + GLOBAL_EMISSION_STATE.set_emitted(&attr_name); + diag.emit(); + + io::stderr().flush().unwrap(); // Flush stderr + false + }, + DeprecationStatus::Replaced(new_name) => { + GLOBAL_EMISSION_STATE.set_emitted(&attr_name); + diag.span_suggestion( + attr_segments[1].ident.span, + "consider using", + new_name, + Applicability::MachineApplicable, + ); + diag.emit(); + + io::stderr().flush().unwrap(); // Flush stderr + false + }, + DeprecationStatus::None => { + diag.cancel(); + attr_segments[1].ident.name.as_str() == name + }, + } } }, ) diff --git a/tests/ui/renamed_builtin_attr.fixed b/tests/ui/renamed_builtin_attr.fixed index aebf8712dd92..bc055215708f 100644 --- a/tests/ui/renamed_builtin_attr.fixed +++ b/tests/ui/renamed_builtin_attr.fixed @@ -1,4 +1,2 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #[clippy::cognitive_complexity = "1"] fn main() {} diff --git a/tests/ui/renamed_builtin_attr.rs b/tests/ui/renamed_builtin_attr.rs index 6c18151195f5..fdb425363e81 100644 --- a/tests/ui/renamed_builtin_attr.rs +++ b/tests/ui/renamed_builtin_attr.rs @@ -1,4 +1,2 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #[clippy::cyclomatic_complexity = "1"] fn main() {} diff --git a/tests/ui/renamed_builtin_attr.stderr b/tests/ui/renamed_builtin_attr.stderr index fb51313dab69..f9108d169c71 100644 --- a/tests/ui/renamed_builtin_attr.stderr +++ b/tests/ui/renamed_builtin_attr.stderr @@ -1,5 +1,5 @@ error: usage of deprecated attribute - --> tests/ui/renamed_builtin_attr.rs:3:11 + --> tests/ui/renamed_builtin_attr.rs:1:11 | LL | #[clippy::cyclomatic_complexity = "1"] | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity`