diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 3c6dbb466db7a..89c59f184e3a3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -192,6 +192,10 @@ lint_redundant_semicolons = *[false] this semicolon } +lint_duplicate_trait = + trait `{$trait_name}` was already specified + .suggestion = remove duplicate trait + lint_drop_trait_constraints = bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped diff --git a/compiler/rustc_lint/src/duplicate_trait.rs b/compiler/rustc_lint/src/duplicate_trait.rs new file mode 100644 index 0000000000000..22b0b70d863b8 --- /dev/null +++ b/compiler/rustc_lint/src/duplicate_trait.rs @@ -0,0 +1,65 @@ +use rustc_data_structures::fx::FxHashSet; + +use crate::hir; + +use crate::{lints::DuplicateTraitDiag, LateContext, LateLintPass}; + +declare_lint! { + /// The `lint_duplicate_trait` lints repetition of traits. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// fn foo(_: &(dyn MyTrait + Send + Send>) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Duplicate trait `Send` in trait object. + pub DUPLICATE_TRAIT, + Warn, + "duplicate trait constraint in trait object" +} + +declare_lint_pass!(DuplicateTrait => [DUPLICATE_TRAIT]); + +impl<'tcx> LateLintPass<'tcx> for DuplicateTrait { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx>) { + let hir::TyKind::Ref( + .., + hir::MutTy { + ty: hir::Ty { + kind: hir::TyKind::TraitObject(bounds, ..), + .. + }, + .. + } + ) = ty.kind else { return; }; + + if bounds.len() < 2 { + return; + } + + let mut seen_def_ids = FxHashSet::default(); + + for bound in bounds.iter() { + let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; + + let already_seen = !seen_def_ids.insert(def_id); + + if already_seen { + cx.tcx.emit_spanned_lint( + DUPLICATE_TRAIT, + bound.trait_ref.hir_ref_id, // is this correct? + bound.span, + DuplicateTraitDiag { + trait_name: cx.tcx.item_name(def_id), + suggestion: bound.span, + }, + ) + } + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 319eb2ea445ed..86e9a5c6a0354 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -52,6 +52,7 @@ mod array_into_iter; pub mod builtin; mod context; mod deref_into_dyn_supertrait; +mod duplicate_trait; mod early; mod enum_intrinsics_non_enums; mod errors; @@ -119,6 +120,7 @@ use unused::*; pub use builtin::SoftLints; pub use context::{CheckLintNameResult, FindLintError, LintStore}; pub use context::{EarlyContext, LateContext, LintContext}; +use duplicate_trait::DuplicateTrait; pub use early::{check_ast_node, EarlyCheckNode}; pub use late::{check_crate, unerased_lint_store}; pub use passes::{EarlyLintPass, LateLintPass}; @@ -242,6 +244,7 @@ late_lint_methods!( OpaqueHiddenInferredBound: OpaqueHiddenInferredBound, MultipleSupertraitUpcastable: MultipleSupertraitUpcastable, MapUnitFn: MapUnitFn, + DuplicateTrait: DuplicateTrait, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 848f6a9ecb532..ab3f70b994086 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1168,6 +1168,15 @@ pub struct RedundantSemicolonsDiag { pub suggestion: Span, } +// duplicate_trait.rs +#[derive(LintDiagnostic)] +#[diag(lint_duplicate_trait)] +pub struct DuplicateTraitDiag { + pub trait_name: Symbol, + #[suggestion(code = "", applicability = "maybe-incorrect")] + pub suggestion: Span, +} + // traits.rs pub struct DropTraitConstraintsDiag<'a> { pub predicate: Predicate<'a>, diff --git a/tests/ui/lint/duplicate-trait/duplicate-trait.rs b/tests/ui/lint/duplicate-trait/duplicate-trait.rs new file mode 100644 index 0000000000000..16b3efe553ece --- /dev/null +++ b/tests/ui/lint/duplicate-trait/duplicate-trait.rs @@ -0,0 +1,16 @@ +// check-fail +#![warn(duplicate_trait)] + +use std::any::Any; + +fn main() {} + +fn fine(_a: &(dyn Any + Send)) {} + +fn duplicate_once(_a: &(dyn Any + Send + Send)) {} //~WARNING duplicate trait + +fn duplicate_twice(_a: &(dyn Any + Send + Send + Send)) {} //~WARNING duplicate trait + +fn duplicate_out_of_order(_a: &(dyn Any + Send + Sync + Send)) {} //~WARNING duplicate trait + +fn duplicate_multiple(_a: &(dyn Any + Send + Sync + Send + Sync)) {} //~WARNING duplicate trait diff --git a/tests/ui/lint/duplicate-trait/duplicate-trait.stderr b/tests/ui/lint/duplicate-trait/duplicate-trait.stderr new file mode 100644 index 0000000000000..906d123b5b053 --- /dev/null +++ b/tests/ui/lint/duplicate-trait/duplicate-trait.stderr @@ -0,0 +1,43 @@ +warning: trait `Send` was already specified + --> $DIR/duplicate-trait.rs:9:42 + | +LL | fn duplicate_once(_a: &(dyn Any + Send + Send)) {} + | ^^^^ help: remove duplicate trait + | +note: the lint level is defined here + --> $DIR/duplicate-trait.rs:1:9 + | +LL | #![warn(duplicate_trait)] + | ^^^^^^^^^^^^^^^ + +warning: trait `Send` was already specified + --> $DIR/duplicate-trait.rs:11:43 + | +LL | fn duplicate_twice(_a: &(dyn Any + Send + Send + Send)) {} + | ^^^^ help: remove duplicate trait + +warning: trait `Send` was already specified + --> $DIR/duplicate-trait.rs:11:50 + | +LL | fn duplicate_twice(_a: &(dyn Any + Send + Send + Send)) {} + | ^^^^ help: remove duplicate trait + +warning: trait `Send` was already specified + --> $DIR/duplicate-trait.rs:13:57 + | +LL | fn duplicate_out_of_order(_a: &(dyn Any + Send + Sync + Send)) {} + | ^^^^ help: remove duplicate trait + +warning: trait `Send` was already specified + --> $DIR/duplicate-trait.rs:15:53 + | +LL | fn duplicate_multiple(_a: &(dyn Any + Send + Sync + Send + Sync)) {} + | ^^^^ help: remove duplicate trait + +warning: trait `Sync` was already specified + --> $DIR/duplicate-trait.rs:15:60 + | +LL | fn duplicate_multiple(_a: &(dyn Any + Send + Sync + Send + Sync)) {} + | ^^^^ help: remove duplicate trait + +warning: 6 warnings emitted