Skip to content

Commit 20376b5

Browse files
Use maybe_unused_trait_imports
1 parent 92f9e59 commit 20376b5

File tree

4 files changed

+40
-171
lines changed

4 files changed

+40
-171
lines changed

clippy_lints/src/anon_trait_import.rs

+7-152
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
use std::ops::ControlFlow;
2-
31
use clippy_utils::diagnostics::span_lint_and_sugg;
42
use clippy_utils::source::snippet_opt;
53
use rustc_errors::Applicability;
64
use rustc_hir::def::{DefKind, Res};
7-
use rustc_hir::def_id::DefId;
8-
use rustc_hir::definitions::DefPathData;
9-
use rustc_hir::intravisit::{walk_item, walk_mod, walk_path, Visitor};
10-
use rustc_hir::{HirId, Item, ItemKind, Node, Path, UseKind};
5+
use rustc_hir::{Item, ItemKind, UseKind};
116
use rustc_lint::{LateContext, LateLintPass};
12-
use rustc_middle::hir::nested_filter;
137
use rustc_middle::ty::Visibility;
148
use rustc_session::declare_lint_pass;
159
use rustc_span::symbol::kw;
@@ -51,17 +45,15 @@ declare_lint_pass!(AnonTraitImport => [ANON_TRAIT_IMPORT]);
5145

5246
impl<'tcx> LateLintPass<'tcx> for AnonTraitImport {
5347
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
54-
let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id);
55-
if cx.tcx.visibility(item.owner_id.def_id) != Visibility::Restricted(module.to_def_id()) {
56-
return;
57-
}
5848
if let ItemKind::Use(path, UseKind::Single) = item.kind
5949
// Ignore imports that already use Underscore
6050
&& item.ident.name != kw::Underscore
61-
&& let Some(Res::Def(DefKind::Trait, def_id)) = path.res.first()
62-
&& let parent_id = cx.tcx.hir().get_parent_item(item.hir_id())
63-
&& let Node::Item(parent_item) = cx.tcx.hir_node_by_def_id(parent_id.def_id)
64-
&& !is_import_used_by_name_in_item(cx, *def_id, parent_item)
51+
// Only check traits
52+
&& let Some(Res::Def(DefKind::Trait, _)) = path.res.first()
53+
&& cx.tcx.maybe_unused_trait_imports(()).contains(&item.owner_id.def_id)
54+
// Only check this use if it is only visible to its module (no pub, pub(crate), ...)
55+
&& let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id)
56+
&& cx.tcx.visibility(item.owner_id.def_id) == Visibility::Restricted(module.to_def_id())
6557
&& let Some(last_segment) = path.segments.last()
6658
&& let Some(snip) = snippet_opt(cx, last_segment.ident.span)
6759
{
@@ -78,140 +70,3 @@ impl<'tcx> LateLintPass<'tcx> for AnonTraitImport {
7870
}
7971
}
8072
}
81-
82-
fn is_import_used_by_name_in_item<'tcx>(cx: &LateContext<'tcx>, def_id: DefId, parent_item: &'tcx Item<'_>) -> bool {
83-
let module = find_module(cx, parent_item);
84-
let mut visitor = TraitUsedByNameVisitor {
85-
cx,
86-
id: def_id,
87-
module,
88-
module_depth: 0,
89-
};
90-
let result = if let ItemKind::Mod(parent_mod) = parent_item.kind {
91-
visitor.visit_mod(parent_mod, parent_item.span, parent_item.hir_id())
92-
} else {
93-
visitor.visit_item(parent_item)
94-
};
95-
matches!(result, ControlFlow::Break(()))
96-
}
97-
98-
fn find_module<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> HirId {
99-
if let ItemKind::Mod(_) = &item.kind {
100-
return item.hir_id();
101-
}
102-
let parent = cx.tcx.hir().get_parent_item(item.hir_id());
103-
let mut node = cx.tcx.hir_node_by_def_id(parent.def_id);
104-
loop {
105-
match node {
106-
Node::Crate(r#mod) => return r#mod.item_ids[0].hir_id(),
107-
Node::Item(item) => {
108-
if let ItemKind::Mod(_) = &item.kind {
109-
return item.hir_id();
110-
}
111-
let hir_id = item.hir_id();
112-
let parent = cx.tcx.hir().get_parent_item(hir_id);
113-
node = cx.tcx.hir_node_by_def_id(parent.def_id);
114-
},
115-
_ => panic!("not an item or crate: {node:?}"),
116-
};
117-
}
118-
}
119-
120-
struct TraitUsedByNameVisitor<'a, 'hir> {
121-
cx: &'a LateContext<'hir>,
122-
id: DefId,
123-
module: HirId,
124-
module_depth: usize,
125-
}
126-
127-
impl<'a, 'hir> Visitor<'hir> for TraitUsedByNameVisitor<'a, 'hir> {
128-
type NestedFilter = nested_filter::All;
129-
type Result = ControlFlow<()>;
130-
131-
fn nested_visit_map(&mut self) -> Self::Map {
132-
self.cx.tcx.hir()
133-
}
134-
135-
fn visit_item(&mut self, item: &'hir Item<'_>) -> Self::Result {
136-
match item.kind {
137-
ItemKind::Mod(m) => {
138-
self.module_depth += 1;
139-
let result = walk_mod(self, m, item.hir_id());
140-
self.module_depth -= 1;
141-
result
142-
},
143-
_ => walk_item(self, item),
144-
}
145-
}
146-
147-
fn visit_path(&mut self, path: &Path<'hir>, _: HirId) -> Self::Result {
148-
if self.module_depth > 0 {
149-
if let Some(segment) = path.segments.first()
150-
&& segment.ident.name == kw::Crate
151-
{
152-
if let Some(mod_segment) = path.segments.get(path.segments.len() - 2)
153-
&& Some(self.module.owner.to_def_id()) == mod_segment.res.mod_def_id()
154-
&& path.res.opt_def_id() == Some(self.id)
155-
{
156-
return ControlFlow::Break(());
157-
}
158-
} else {
159-
let mut super_count = 0;
160-
for segment in path.segments {
161-
if segment.ident.name == kw::Super {
162-
super_count += 1;
163-
if super_count > self.module_depth {
164-
break;
165-
}
166-
} else {
167-
if super_count == self.module_depth
168-
&& (path.res.opt_def_id() == Some(self.id) || segment.res.opt_def_id() == Some(self.id))
169-
{
170-
return ControlFlow::Break(());
171-
}
172-
break;
173-
}
174-
}
175-
}
176-
}
177-
if let Some(def_id) = self.first_path_segment_def_id(path)
178-
&& def_id == self.id
179-
&& self.module_depth == 0
180-
{
181-
return ControlFlow::Break(());
182-
}
183-
walk_path(self, path)
184-
}
185-
}
186-
187-
impl<'hir> TraitUsedByNameVisitor<'_, 'hir> {
188-
fn skip_def_id(&self, def_id: DefId) -> DefId {
189-
let def_key = self.cx.tcx.def_key(def_id);
190-
match def_key.disambiguated_data.data {
191-
DefPathData::Ctor => {
192-
if let Some(def_id) = self.cx.tcx.opt_parent(def_id) {
193-
self.skip_def_id(def_id)
194-
} else {
195-
def_id
196-
}
197-
},
198-
_ => def_id,
199-
}
200-
}
201-
202-
fn first_path_segment_def_id(&self, path: &Path<'_>) -> Option<DefId> {
203-
path.res.opt_def_id().and_then(|mut def_id| {
204-
def_id = self.skip_def_id(def_id);
205-
for _ in path.segments.iter().skip(1) {
206-
def_id = self.skip_def_id(def_id);
207-
if let Some(parent_def_id) = self.cx.tcx.opt_parent(def_id) {
208-
def_id = parent_def_id;
209-
} else {
210-
return None;
211-
}
212-
}
213-
214-
Some(def_id)
215-
})
216-
}
217-
}

tests/ui/anon_trait_import.fixed

+17-10
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ mod nested_mod_used_good3 {
102102
}
103103
}
104104

105-
mod nested_mod_used_not_used_bad {
105+
mod nested_mod_used_bad {
106106
use std::any::Any as _;
107107

108108
fn bar() {
@@ -118,15 +118,18 @@ mod nested_mod_used_not_used_bad {
118118
}
119119
}
120120

121-
// Known limitation
122-
// This is currently a false negative because if we find any `use crate::...` for the trait then we
123-
// assume we can't anonymise the trait import
124-
/*
121+
// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as
122+
// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;`
123+
// the code would still compile.
125124
mod nested_mod_used_bad1 {
126-
use std::any::Any;
125+
use std::any::Any as _;
127126

128127
use std::any::Any as MyAny;
129128

129+
fn baz() {
130+
println!("{:?}", "baz".type_id());
131+
}
132+
130133
mod foo {
131134
use crate::nested_mod_used_bad1::MyAny;
132135

@@ -135,16 +138,20 @@ mod nested_mod_used_bad1 {
135138
}
136139
}
137140
}
138-
*/
139141

140-
mod nested_mod_used_bad2 {
141-
use std::any::Any as _;
142+
// Example of nested import with an unused import to try and trick it
143+
mod nested_mod_used_good5 {
144+
use std::any::Any;
142145

143146
mod foo {
144147
use std::any::Any;
145148

149+
fn baz() {
150+
println!("{:?}", "baz".type_id());
151+
}
152+
146153
mod bar {
147-
use crate::nested_mod_used_bad2::foo::Any;
154+
use crate::nested_mod_used_good5::foo::Any;
148155

149156
fn foo() {
150157
println!("{:?}", Any::type_id("foo"));

tests/ui/anon_trait_import.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ mod nested_mod_used_good3 {
102102
}
103103
}
104104

105-
mod nested_mod_used_not_used_bad {
105+
mod nested_mod_used_bad {
106106
use std::any::Any;
107107

108108
fn bar() {
@@ -118,15 +118,18 @@ mod nested_mod_used_not_used_bad {
118118
}
119119
}
120120

121-
// Known limitation
122-
// This is currently a false negative because if we find any `use crate::...` for the trait then we
123-
// assume we can't anonymise the trait import
124-
/*
121+
// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as
122+
// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;`
123+
// the code would still compile.
125124
mod nested_mod_used_bad1 {
126125
use std::any::Any;
127126

128127
use std::any::Any as MyAny;
129128

129+
fn baz() {
130+
println!("{:?}", "baz".type_id());
131+
}
132+
130133
mod foo {
131134
use crate::nested_mod_used_bad1::MyAny;
132135

@@ -135,16 +138,20 @@ mod nested_mod_used_bad1 {
135138
}
136139
}
137140
}
138-
*/
139141

140-
mod nested_mod_used_bad2 {
142+
// Example of nested import with an unused import to try and trick it
143+
mod nested_mod_used_good5 {
141144
use std::any::Any;
142145

143146
mod foo {
144147
use std::any::Any;
145148

149+
fn baz() {
150+
println!("{:?}", "baz".type_id());
151+
}
152+
146153
mod bar {
147-
use crate::nested_mod_used_bad2::foo::Any;
154+
use crate::nested_mod_used_good5::foo::Any;
148155

149156
fn foo() {
150157
println!("{:?}", Any::type_id("foo"));

tests/ui/anon_trait_import.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ LL | use std::any::Any;
3838
| ^^^ help: use: `Any as _`
3939

4040
error: importing trait that is only used anonymously
41-
--> tests/ui/anon_trait_import.rs:141:19
41+
--> tests/ui/anon_trait_import.rs:125:19
4242
|
4343
LL | use std::any::Any;
4444
| ^^^ help: use: `Any as _`

0 commit comments

Comments
 (0)