Skip to content

new lint: needless traits in scope #10503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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`
}

Expand Down
86 changes: 86 additions & 0 deletions clippy_lints/src/needless_traits_in_scope.rs
Original file line number Diff line number Diff line change
@@ -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]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll want to write your own struct so you can impl_lint_pass! here instead. Then you can add a Visitor to your LintPass that you use on check_crate to run through the code and collect any trait use. Please leave a comment or ping me on zulip if you need help with that.


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,
);
}
}
52 changes: 52 additions & 0 deletions tests/ui/needless_traits_in_scope.fixed
Original file line number Diff line number Diff line change
@@ -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<usize> {
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<usize> {
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<usize> {
// 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();
}
52 changes: 52 additions & 0 deletions tests/ui/needless_traits_in_scope.rs
Original file line number Diff line number Diff line change
@@ -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<usize> {
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<usize> {
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<usize> {
// 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();
}
10 changes: 10 additions & 0 deletions tests/ui/needless_traits_in_scope.stderr
Original file line number Diff line number Diff line change
@@ -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