Skip to content

Commit abdf173

Browse files
committed
Auto merge of #13322 - RuairidhWilliamson:anon-trait-import, r=y21
Unused trait imports (formerly anonymous trait import) For #11969 I'm looking for help and feedback on implementing a new lint for suggesting `use ... as _` for traits where possible. I have had a go at implementing this but I don't know if this is the best way to do it as I am new to clippy. There are some edge cases I can think of where this doesn't work but have aired on the side of false negatives instead of false positives. An example of a false negative. I couldn't figure out the best way to resolve an import from within clippy. The sub module imports MyAny so that cannot be anonymized but `use std::any::Any` could be. In this case it is not caught because `Any` and `MyAny` have the same DefId. ```rust mod nested_mod_used_bad1 { use std::any::Any; use std::any::Any as MyAny; mod foo { use crate::nested_mod_used_bad1::MyAny; fn foo() { println!("{:?}", MyAny::type_id("foo")); } } } ``` Any feedback is much appreciated. ------- changelog: new lint: `unused_trait_names`
2 parents 612ae35 + 739ef7b commit abdf173

11 files changed

+747
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6063,6 +6063,7 @@ Released 2018-09-13
60636063
[`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok
60646064
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
60656065
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
6066+
[`unused_trait_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names
60666067
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
60676068
[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
60686069
[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result

book/src/lint_configuration.md

+1
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
727727
* [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
728728
* [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)
729729
* [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
730+
* [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names)
730731
* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
731732

732733

clippy_config/src/conf.rs

+1
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ define_Conf! {
563563
uninlined_format_args,
564564
unnecessary_lazy_evaluations,
565565
unnested_or_patterns,
566+
unused_trait_names,
566567
use_self,
567568
)]
568569
msrv: Msrv = Msrv::empty(),

clippy_config/src/msrvs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ msrv_aliases! {
4949
1,36,0 { ITERATOR_COPIED }
5050
1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
5151
1,34,0 { TRY_FROM }
52+
1,33,0 { UNDERSCORE_IMPORTS }
5253
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
5354
1,29,0 { ITER_FLATTEN }
5455
1,28,0 { FROM_BOOL }

clippy_lints/src/attrs/useless_attribute.rs

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
5151
| "module_name_repetitions"
5252
| "single_component_path_imports"
5353
| "disallowed_types"
54+
| "unused_trait_names"
5455
)
5556
}) {
5657
return;

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
748748
crate::unused_result_ok::UNUSED_RESULT_OK_INFO,
749749
crate::unused_rounding::UNUSED_ROUNDING_INFO,
750750
crate::unused_self::UNUSED_SELF_INFO,
751+
crate::unused_trait_names::UNUSED_TRAIT_NAMES_INFO,
751752
crate::unused_unit::UNUSED_UNIT_INFO,
752753
crate::unwrap::PANICKING_UNWRAP_INFO,
753754
crate::unwrap::UNNECESSARY_UNWRAP_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ mod unused_peekable;
376376
mod unused_result_ok;
377377
mod unused_rounding;
378378
mod unused_self;
379+
mod unused_trait_names;
379380
mod unused_unit;
380381
mod unwrap;
381382
mod unwrap_in_result;
@@ -942,5 +943,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
942943
store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf)));
943944
store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
944945
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
946+
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
945947
// add lints here, do not remove this comment, it's used in `new_lint`
946948
}
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use clippy_config::msrvs::{self, Msrv};
2+
use clippy_config::Conf;
3+
use clippy_utils::diagnostics::span_lint_and_sugg;
4+
use clippy_utils::is_from_proc_macro;
5+
use clippy_utils::source::snippet_opt;
6+
use rustc_errors::Applicability;
7+
use rustc_hir::def::{DefKind, Res};
8+
use rustc_hir::{Item, ItemKind, UseKind};
9+
use rustc_lint::{LateContext, LateLintPass, LintContext as _};
10+
use rustc_middle::lint::in_external_macro;
11+
use rustc_middle::ty::Visibility;
12+
use rustc_session::impl_lint_pass;
13+
use rustc_span::symbol::kw;
14+
15+
declare_clippy_lint! {
16+
/// ### What it does
17+
/// Checks for `use Trait` where the Trait is only used for its methods and not referenced by a path directly.
18+
///
19+
/// ### Why is this bad?
20+
/// Traits imported that aren't used directly can be imported anonymously with `use Trait as _`.
21+
/// It is more explicit, avoids polluting the current scope with unused names and can be useful to show which imports are required for traits.
22+
///
23+
/// ### Example
24+
/// ```no_run
25+
/// use std::fmt::Write;
26+
///
27+
/// fn main() {
28+
/// let mut s = String::new();
29+
/// let _ = write!(s, "hello, world!");
30+
/// println!("{s}");
31+
/// }
32+
/// ```
33+
/// Use instead:
34+
/// ```no_run
35+
/// use std::fmt::Write as _;
36+
///
37+
/// fn main() {
38+
/// let mut s = String::new();
39+
/// let _ = write!(s, "hello, world!");
40+
/// println!("{s}");
41+
/// }
42+
/// ```
43+
#[clippy::version = "1.83.0"]
44+
pub UNUSED_TRAIT_NAMES,
45+
restriction,
46+
"use items that import a trait but only use it anonymously"
47+
}
48+
49+
pub struct UnusedTraitNames {
50+
msrv: Msrv,
51+
}
52+
53+
impl UnusedTraitNames {
54+
pub fn new(conf: &'static Conf) -> Self {
55+
Self {
56+
msrv: conf.msrv.clone(),
57+
}
58+
}
59+
}
60+
61+
impl_lint_pass!(UnusedTraitNames => [UNUSED_TRAIT_NAMES]);
62+
63+
impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames {
64+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
65+
if self.msrv.meets(msrvs::UNDERSCORE_IMPORTS)
66+
&& !in_external_macro(cx.sess(), item.span)
67+
&& let ItemKind::Use(path, UseKind::Single) = item.kind
68+
// Ignore imports that already use Underscore
69+
&& item.ident.name != kw::Underscore
70+
// Only check traits
71+
&& let Some(Res::Def(DefKind::Trait, _)) = path.res.first()
72+
&& cx.tcx.maybe_unused_trait_imports(()).contains(&item.owner_id.def_id)
73+
// Only check this import if it is visible to its module only (no pub, pub(crate), ...)
74+
&& let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id)
75+
&& cx.tcx.visibility(item.owner_id.def_id) == Visibility::Restricted(module.to_def_id())
76+
&& let Some(last_segment) = path.segments.last()
77+
&& let Some(snip) = snippet_opt(cx, last_segment.ident.span)
78+
&& !is_from_proc_macro(cx, &last_segment.ident)
79+
{
80+
let complete_span = last_segment.ident.span.to(item.ident.span);
81+
span_lint_and_sugg(
82+
cx,
83+
UNUSED_TRAIT_NAMES,
84+
complete_span,
85+
"importing trait that is only used anonymously",
86+
"use",
87+
format!("{snip} as _"),
88+
Applicability::MachineApplicable,
89+
);
90+
}
91+
}
92+
93+
extract_msrv_attr!(LateContext);
94+
}

0 commit comments

Comments
 (0)