diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 67ba605a59fa5..1296b59f2e1a0 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -51,6 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) | "module_name_repetitions" | "single_component_path_imports" | "disallowed_types" + | "unused_trait_names" ) }) { return; diff --git a/tests/ui/unused_trait_names.fixed b/tests/ui/unused_trait_names.fixed new file mode 100644 index 0000000000000..7dfd0db65aa1b --- /dev/null +++ b/tests/ui/unused_trait_names.fixed @@ -0,0 +1,286 @@ +//@aux-build:proc_macros.rs + +#![allow(unused)] +#![warn(clippy::unused_trait_names)] +#![feature(decl_macro)] + +extern crate proc_macros; + +fn main() {} + +fn bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn good() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn used_good() { + use std::any::Any; + + println!("{:?}", Any::type_id("foo")); + println!("{:?}", "foo".type_id()); +} + +fn multi_bad() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn multi_good() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn renamed_bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn multi_renamed_bad() { + use std::any::{Any as _, TypeId as MyTypeId}; + + println!("{:?}", "foo".type_id()); +} + +mod pub_good { + pub use std::any::Any; + + fn foo() { + println!("{:?}", "foo".type_id()); + } +} + +mod used_mod_good { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } +} + +mod mod_import_bad { + fn mod_import_bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); + } +} + +mod nested_mod_used_good1 { + use std::any::Any; + + mod foo { + fn foo() { + super::Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good2 { + use std::any::Any; + + mod foo { + use super::Any; + + fn foo() { + Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good3 { + use std::any::Any; + + mod foo { + use crate::nested_mod_used_good3::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +mod nested_mod_used_bad { + use std::any::Any as _; + + fn bar() { + println!("{:?}", "foo".type_id()); + } + + mod foo { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as +// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;` +// the code would still compile. +mod nested_mod_used_bad1 { + use std::any::Any as _; + + use std::any::Any as MyAny; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod foo { + use crate::nested_mod_used_bad1::MyAny; + + fn foo() { + println!("{:?}", MyAny::type_id("foo")); + } + } +} + +// Example of nested import with an unused import to try and trick it +mod nested_mod_used_good5 { + use std::any::Any; + + mod foo { + use std::any::Any; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod bar { + use crate::nested_mod_used_good5::foo::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } + } +} + +mod simple_trait { + pub trait MyTrait { + fn do_things(&self); + } + + pub struct MyStruct; + + impl MyTrait for MyStruct { + fn do_things(&self) {} + } +} + +// Underscore imports were stabilized in 1.33 +#[clippy::msrv = "1.32"] +fn msrv_1_32() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +#[clippy::msrv = "1.33"] +fn msrv_1_33() { + use simple_trait::{MyStruct, MyTrait as _}; + MyStruct.do_things(); +} + +mod lint_inside_macro_expansion_bad { + macro_rules! foo { + () => { + use std::any::Any as _; + fn bar() { + "bar".type_id(); + } + }; + } + + foo!(); +} + +mod macro_and_trait_same_name { + pub macro Foo() {} + pub trait Foo { + fn bar(&self); + } + impl Foo for () { + fn bar(&self) {} + } +} + +fn call_macro_and_trait_good() { + // importing trait and macro but only using macro by path won't allow us to change this to + // `use macro_and_trait_same_name::Foo as _;` + use macro_and_trait_same_name::Foo; + Foo!(); + ().bar(); +} + +proc_macros::external!( + fn ignore_inside_external_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +proc_macros::with_span!( + span + + fn ignore_inside_with_span_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +// This should warn the import is unused but should not trigger unused_trait_names +#[warn(unused)] +mod unused_import { + +} + +#[allow(clippy::unused_trait_names)] +fn allow_lint_fn() { + use std::any::Any; + + "foo".type_id(); +} + +#[allow(clippy::unused_trait_names)] +mod allow_lint_mod { + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +mod allow_lint_import { + #[allow(clippy::unused_trait_names)] + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +// Limitation: Suggests `use std::any::Any as _::{self};` which looks weird +// fn use_trait_self_good() { +// use std::any::Any::{self}; +// "foo".type_id(); +// } + +// Limitation: Suggests `use std::any::{Any as _, Any as _};` +// mod repeated_renamed { +// use std::any::{Any, Any as MyAny}; + +// fn foo() { +// "foo".type_id(); +// } +// } diff --git a/tests/ui/unused_trait_names.rs b/tests/ui/unused_trait_names.rs new file mode 100644 index 0000000000000..ce44eedbc79c2 --- /dev/null +++ b/tests/ui/unused_trait_names.rs @@ -0,0 +1,286 @@ +//@aux-build:proc_macros.rs + +#![allow(unused)] +#![warn(clippy::unused_trait_names)] +#![feature(decl_macro)] + +extern crate proc_macros; + +fn main() {} + +fn bad() { + use std::any::Any; + + println!("{:?}", "foo".type_id()); +} + +fn good() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn used_good() { + use std::any::Any; + + println!("{:?}", Any::type_id("foo")); + println!("{:?}", "foo".type_id()); +} + +fn multi_bad() { + use std::any::{self, Any, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn multi_good() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn renamed_bad() { + use std::any::Any as MyAny; + + println!("{:?}", "foo".type_id()); +} + +fn multi_renamed_bad() { + use std::any::{Any as MyAny, TypeId as MyTypeId}; + + println!("{:?}", "foo".type_id()); +} + +mod pub_good { + pub use std::any::Any; + + fn foo() { + println!("{:?}", "foo".type_id()); + } +} + +mod used_mod_good { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } +} + +mod mod_import_bad { + fn mod_import_bad() { + use std::any::Any; + + println!("{:?}", "foo".type_id()); + } +} + +mod nested_mod_used_good1 { + use std::any::Any; + + mod foo { + fn foo() { + super::Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good2 { + use std::any::Any; + + mod foo { + use super::Any; + + fn foo() { + Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good3 { + use std::any::Any; + + mod foo { + use crate::nested_mod_used_good3::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +mod nested_mod_used_bad { + use std::any::Any; + + fn bar() { + println!("{:?}", "foo".type_id()); + } + + mod foo { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as +// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;` +// the code would still compile. +mod nested_mod_used_bad1 { + use std::any::Any; + + use std::any::Any as MyAny; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod foo { + use crate::nested_mod_used_bad1::MyAny; + + fn foo() { + println!("{:?}", MyAny::type_id("foo")); + } + } +} + +// Example of nested import with an unused import to try and trick it +mod nested_mod_used_good5 { + use std::any::Any; + + mod foo { + use std::any::Any; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod bar { + use crate::nested_mod_used_good5::foo::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } + } +} + +mod simple_trait { + pub trait MyTrait { + fn do_things(&self); + } + + pub struct MyStruct; + + impl MyTrait for MyStruct { + fn do_things(&self) {} + } +} + +// Underscore imports were stabilized in 1.33 +#[clippy::msrv = "1.32"] +fn msrv_1_32() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +#[clippy::msrv = "1.33"] +fn msrv_1_33() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +mod lint_inside_macro_expansion_bad { + macro_rules! foo { + () => { + use std::any::Any; + fn bar() { + "bar".type_id(); + } + }; + } + + foo!(); +} + +mod macro_and_trait_same_name { + pub macro Foo() {} + pub trait Foo { + fn bar(&self); + } + impl Foo for () { + fn bar(&self) {} + } +} + +fn call_macro_and_trait_good() { + // importing trait and macro but only using macro by path won't allow us to change this to + // `use macro_and_trait_same_name::Foo as _;` + use macro_and_trait_same_name::Foo; + Foo!(); + ().bar(); +} + +proc_macros::external!( + fn ignore_inside_external_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +proc_macros::with_span!( + span + + fn ignore_inside_with_span_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +// This should warn the import is unused but should not trigger unused_trait_names +#[warn(unused)] +mod unused_import { + use std::any::Any; +} + +#[allow(clippy::unused_trait_names)] +fn allow_lint_fn() { + use std::any::Any; + + "foo".type_id(); +} + +#[allow(clippy::unused_trait_names)] +mod allow_lint_mod { + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +mod allow_lint_import { + #[allow(clippy::unused_trait_names)] + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +// Limitation: Suggests `use std::any::Any as _::{self};` which looks weird +// fn use_trait_self_good() { +// use std::any::Any::{self}; +// "foo".type_id(); +// } + +// Limitation: Suggests `use std::any::{Any as _, Any as _};` +// mod repeated_renamed { +// use std::any::{Any, Any as MyAny}; + +// fn foo() { +// "foo".type_id(); +// } +// } diff --git a/tests/ui/unused_trait_names.stderr b/tests/ui/unused_trait_names.stderr new file mode 100644 index 0000000000000..f59d8f58a170a --- /dev/null +++ b/tests/ui/unused_trait_names.stderr @@ -0,0 +1,73 @@ +error: unused import: `std::any::Any` + --> tests/ui/unused_trait_names.rs:245:9 + | +LL | use std::any::Any; + | ^^^^^^^^^^^^^ + | + = note: `-D unused-imports` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_imports)]` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:12:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + | + = note: `-D clippy::unused-trait-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unused_trait_names)]` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:31:26 + | +LL | use std::any::{self, Any, TypeId}; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:43:19 + | +LL | use std::any::Any as MyAny; + | ^^^^^^^^^^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:49:20 + | +LL | use std::any::{Any as MyAny, TypeId as MyTypeId}; + | ^^^^^^^^^^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:72:23 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:113:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:132:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:191:34 + | +LL | use simple_trait::{MyStruct, MyTrait}; + | ^^^^^^^ help: use: `MyTrait as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:198:27 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` +... +LL | foo!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors +