diff --git a/CHANGELOG.md b/CHANGELOG.md index da7042f44400..d921d0b59f34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4759,6 +4759,7 @@ Released 2018-09-13 [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return [`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn +[`needless_traits_in_scope`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_traits_in_scope [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord [`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index cc6024b87cda..b8ffb63cb5e8 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -446,6 +446,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO, + crate::needless_traits_in_scope::NEEDLESS_TRAITS_IN_SCOPE_INFO, crate::needless_update::NEEDLESS_UPDATE_INFO, crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO, crate::neg_multiply::NEG_MULTIPLY_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bde84686cc1b..1608ae8d9401 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -218,6 +218,7 @@ mod needless_late_init; mod needless_parens_on_range_literals; mod needless_pass_by_value; mod needless_question_mark; +mod needless_traits_in_scope; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -934,6 +935,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); + store.register_late_pass(|_| Box::new(needless_traits_in_scope::NeedlessTraitsInScope)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/needless_traits_in_scope.rs b/clippy_lints/src/needless_traits_in_scope.rs new file mode 100644 index 000000000000..4696f9fc7cbc --- /dev/null +++ b/clippy_lints/src/needless_traits_in_scope.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Find traits that are not explicitely used in scope (like `AsRef::as_ref()`) + /// but directly with the trait method (like `opt.as_ref()`). + /// + /// These traits can be imported anonymously with `use crate::Trait as _`. + /// This avoids name collision with other traits (possibly with the same name). + /// It also helps identify the traits in `use` statements. + /// + /// ### Why is this bad? + /// This needlessly brings a trait into the type namespace, where it could + /// shadow other things. This is not really a problem, this lint is just + /// for those who like to keep things tidy. + /// + /// ### Example + /// ```rust + /// use std::io::Read; + /// fn main() { + /// let mut b = "I'm your father!".as_bytes(); + /// let mut buffer = [0; 10]; + /// b.read(&mut buffer) + /// } + /// ``` + /// Use instead: + /// ```rust + /// use std::io::Read as _; + /// fn main() { + /// let mut b = "I'm your father!".as_bytes(); + /// let mut buffer = [0; 10]; + /// b.read(&mut buffer) + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub NEEDLESS_TRAITS_IN_SCOPE, + restriction, + "trait is needlessly imported into the type namespace, and can be anonymously imported" +} +declare_lint_pass!(NeedlessTraitsInScope => [NEEDLESS_TRAITS_IN_SCOPE]); + +impl<'tcx> LateLintPass<'tcx> for NeedlessTraitsInScope { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + // Only process `use` statements, ignore `UseKind::Glob` + let ItemKind::Use(use_path, UseKind::Single) = item.kind else { + return + }; + // Check if it's a trait + if !use_path + .res + .iter() + .any(|res| matches!(res, def::Res::Def(def::DefKind::Trait, _))) + { + return; + } + // Check if the `use` is aliased with ` as `. + // If aliased, then do not process, it's probably for a good reason + if item.ident != use_path.segments.last().unwrap().ident { + return; + } + let path = use_path + .segments + .iter() + .map(|segment| segment.ident) + .fold(String::new(), |mut acc, ident| { + if !acc.is_empty() { + acc += "::"; + } + acc += ident.as_str(); + acc + }); + span_lint_and_sugg( + cx, + NEEDLESS_TRAITS_IN_SCOPE, + use_path.span, + "trait is needlessly imported into the type namespace", + "you can import the trait anonymously", + format!("{path} as _"), + Applicability::MachineApplicable, + ); + } +} diff --git a/tests/ui/needless_traits_in_scope.fixed b/tests/ui/needless_traits_in_scope.fixed new file mode 100644 index 000000000000..6707331badb1 --- /dev/null +++ b/tests/ui/needless_traits_in_scope.fixed @@ -0,0 +1,52 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::needless_traits_in_scope)] + +pub mod useless_trait_in_scope { + use std::io::Read as _; + + pub fn warn() -> std::io::Result { + let mut b = "The trait is not used explicitely -> 'use std::io::Read' doesn't need to be in scope".as_bytes(); + let mut buffer = [0; 10]; + b.read(&mut buffer) + } +} + +pub mod trait_not_in_scope { + use std::io::Read as _; + + pub fn ok() -> std::io::Result { + let mut b = "The trait is not used explicitely, but 'use std::io::Read' is already not in scope".as_bytes(); + let mut buffer = [0; 10]; + b.read(&mut buffer) + } +} + +pub mod is_not_a_trait { + mod inner { + pub struct Read; + } + use inner::Read; + + pub fn ok() { + let _ = Read; + } +} + +// FIXME: when the trait is explicitely used, the lint should not trigger +// pub mod useful_trait_in_scope { +// use std::io::Read; + +// pub fn ok() -> std::io::Result { +// let mut b = "Trait is used explicitely -> 'use std::io::Read' is OK".as_bytes(); +// let mut buffer = [0; 10]; +// Read::read(&mut b, &mut buffer) +// } +// } + +fn main() { + useless_trait_in_scope::warn(); + trait_not_in_scope::ok(); + is_not_a_trait::ok(); + // useful_trait_in_scope::ok(); +} diff --git a/tests/ui/needless_traits_in_scope.rs b/tests/ui/needless_traits_in_scope.rs new file mode 100644 index 000000000000..0fb865e38ab7 --- /dev/null +++ b/tests/ui/needless_traits_in_scope.rs @@ -0,0 +1,52 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::needless_traits_in_scope)] + +pub mod useless_trait_in_scope { + use std::io::Read; + + pub fn warn() -> std::io::Result { + let mut b = "The trait is not used explicitely -> 'use std::io::Read' doesn't need to be in scope".as_bytes(); + let mut buffer = [0; 10]; + b.read(&mut buffer) + } +} + +pub mod trait_not_in_scope { + use std::io::Read as _; + + pub fn ok() -> std::io::Result { + let mut b = "The trait is not used explicitely, but 'use std::io::Read' is already not in scope".as_bytes(); + let mut buffer = [0; 10]; + b.read(&mut buffer) + } +} + +pub mod is_not_a_trait { + mod inner { + pub struct Read; + } + use inner::Read; + + pub fn ok() { + let _ = Read; + } +} + +// FIXME: when the trait is explicitely used, the lint should not trigger +// pub mod useful_trait_in_scope { +// use std::io::Read; + +// pub fn ok() -> std::io::Result { +// let mut b = "Trait is used explicitely -> 'use std::io::Read' is OK".as_bytes(); +// let mut buffer = [0; 10]; +// Read::read(&mut b, &mut buffer) +// } +// } + +fn main() { + useless_trait_in_scope::warn(); + trait_not_in_scope::ok(); + is_not_a_trait::ok(); + // useful_trait_in_scope::ok(); +} diff --git a/tests/ui/needless_traits_in_scope.stderr b/tests/ui/needless_traits_in_scope.stderr new file mode 100644 index 000000000000..975a96fb6b39 --- /dev/null +++ b/tests/ui/needless_traits_in_scope.stderr @@ -0,0 +1,10 @@ +error: trait is needlessly imported in trait's namespace + --> $DIR/needless_traits_in_scope.rs:6:9 + | +LL | use std::io::Read; + | ^^^^^^^^^^^^^ help: you can import the trait anonymously: `std::io::Read as _` + | + = note: `-D clippy::needless-traits-in-scope` implied by `-D warnings` + +error: aborting due to previous error +