Skip to content

Commit 1b5d4e6

Browse files
committed
check redundant prelude imports
detects unnecessary imports in std::prelude that can be eliminated. For example import: ```rust use std::{option::{Iter, IterMut, IntoIter, Option::{self, Some}}, convert::{TryFrom, TryInto}, mem::drop}; ``` delete : `Option::{self, Some}` and `mem::drop`
1 parent f440b5f commit 1b5d4e6

File tree

99 files changed

+1369
-205
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+1369
-205
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
use crate::{lints::RedundantPreludeImportsDiag, EarlyContext, EarlyLintPass, LintContext};
2+
use rustc_ast as ast;
3+
use rustc_span::symbol::{sym, Symbol};
4+
use rustc_span::Span;
5+
use std::cell::RefCell;
6+
use std::collections::BTreeMap;
7+
use std::rc::Rc;
8+
use std::string::ToString;
9+
10+
declare_lint! {
11+
/// The `redundant_prelude_imports` lint detects unnecessary imports in
12+
/// std::prelude that can be eliminated.
13+
///
14+
/// ### Example
15+
///
16+
/// ```rust
17+
/// # #![allow(unused)]
18+
/// # #![warn(redundant_prelude_imports)]
19+
/// use std::mem::drop;
20+
/// ```
21+
///
22+
/// {{produces}}
23+
///
24+
/// ### Explanation
25+
///
26+
/// unnecessary prelude import 'std::mem::drop'
27+
pub(super) REDUNDANT_PRELUDE_IMPORTS,
28+
Allow,
29+
"detects unnecessary imports in std::prelude that can be eliminated"
30+
}
31+
32+
declare_lint_pass!(RedundantPreludeImports => [REDUNDANT_PRELUDE_IMPORTS]);
33+
34+
#[derive(Debug)]
35+
struct RedundantImport<'a, 'b> {
36+
item: &'a ast::Item,
37+
prelude_imports: &'b BTreeMap<&'b str, Symbol>,
38+
remove_imports: Vec<(Span, Vec<String>)>,
39+
reserve_imports: Vec<(Span, Vec<String>)>,
40+
}
41+
42+
impl EarlyLintPass for RedundantPreludeImports {
43+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
44+
if let ast::ItemKind::Use(ref use_tree) = item.kind {
45+
// Use in std::prelude
46+
let prelude_imports: BTreeMap<&str, Symbol> = BTreeMap::from([
47+
("std::marker::Copy", sym::rust_2015),
48+
("std::marker::Send", sym::rust_2015),
49+
("std::marker::Sized", sym::rust_2015),
50+
("std::marker::Sync", sym::rust_2015),
51+
("std::marker::Unpin", sym::rust_2015),
52+
("std::ops::Drop", sym::rust_2015),
53+
("std::ops::Fn", sym::rust_2015),
54+
("std::ops::FnMut", sym::rust_2015),
55+
("std::ops::FnOnce", sym::rust_2015),
56+
("std::mem::drop", sym::rust_2015),
57+
("std::boxed::Box", sym::rust_2015),
58+
("std::borrow::ToOwned", sym::rust_2015),
59+
("std::clone::Clone", sym::rust_2015),
60+
("std::cmp::PartialEq", sym::rust_2015),
61+
("std::cmp::PartialOrd", sym::rust_2015),
62+
("std::cmp::Eq", sym::rust_2015),
63+
("std::cmp::Ord", sym::rust_2015),
64+
("std::convert::AsRef", sym::rust_2015),
65+
("std::convert::AsMut", sym::rust_2015),
66+
("std::convert::Into", sym::rust_2015),
67+
("std::convert::From", sym::rust_2015),
68+
("std::default::Default", sym::rust_2015),
69+
("std::iter::Iterator", sym::rust_2015),
70+
("std::iter::Extend", sym::rust_2015),
71+
("std::iter::IntoIterator", sym::rust_2015),
72+
("std::iter::DoubleEndedIterator", sym::rust_2015),
73+
("std::iter::ExactSizeIterator", sym::rust_2015),
74+
("std::option::Option::self", sym::rust_2015),
75+
("std::option::Option::Some", sym::rust_2015),
76+
("std::option::Option::None", sym::rust_2015),
77+
("std::result::Result", sym::rust_2015),
78+
("std::result::Result::Ok", sym::rust_2015),
79+
("std::result::Result::Err", sym::rust_2015),
80+
("std::string::String", sym::rust_2015),
81+
("std::string::ToString", sym::rust_2015),
82+
("std::vec::Vec", sym::rust_2015),
83+
("std::convert::TryFrom", sym::rust_2018),
84+
("std::convert::TryInto", sym::rust_2018),
85+
("std::iter::FromIterator", sym::rust_2018),
86+
]);
87+
let mut redundant = RedundantImport {
88+
item: item,
89+
prelude_imports: &prelude_imports,
90+
remove_imports: vec![],
91+
reserve_imports: vec![],
92+
};
93+
let path = vec![];
94+
self.check_use_tree(cx, use_tree, &path, &mut redundant);
95+
if redundant.remove_imports.len() > 0 {
96+
// Delete the usetrees imported by std::prelude.
97+
// Use the prefix tree to make suggestion msg for the remaining ones.
98+
// For import :
99+
// use std::{option::{Iter, IterMut, IntoIter, Option::{self, Some}},
100+
// convert::{TryFrom, TryInto}, mem::drop};
101+
// Replace to:
102+
// use std::{option::{Iter, IterMut, IntoIter}, convert::{TryFrom, TryInto}};
103+
let replace = if redundant.reserve_imports.len() == 0 {
104+
"".to_string()
105+
} else {
106+
let mut tree =
107+
MakeSuggestionTree::new(MakeSuggestionNode::new("std".to_string()));
108+
redundant.reserve_imports.iter().for_each(|v| {
109+
tree.add_node(&v.1);
110+
});
111+
let mut use_replace = "use ".to_string();
112+
use_replace.push_str(&tree.make_suggestion());
113+
use_replace.push(';');
114+
use_replace
115+
};
116+
let lint_msg =
117+
&redundant.remove_imports.into_iter().fold("".to_string(), |mut acc, x| {
118+
acc.push_str(" '");
119+
acc.push_str(
120+
&*cx.sess().parse_sess.source_map().span_to_snippet(x.0).unwrap(),
121+
);
122+
acc.push_str("'");
123+
acc
124+
});
125+
cx.emit_spanned_lint(
126+
REDUNDANT_PRELUDE_IMPORTS,
127+
item.span,
128+
RedundantPreludeImportsDiag {
129+
redundant_crates: lint_msg.to_string(),
130+
suggestion: item.span,
131+
replace: replace,
132+
},
133+
);
134+
}
135+
}
136+
}
137+
}
138+
139+
#[derive(Debug)]
140+
struct MakeSuggestionNode {
141+
prefix: String,
142+
child: Rc<RefCell<Vec<MakeSuggestionNode>>>,
143+
}
144+
145+
impl MakeSuggestionNode {
146+
fn new(prefix: String) -> Self {
147+
Self { prefix: prefix, child: Rc::new(RefCell::new(vec![])) }
148+
}
149+
150+
fn add_child(&mut self, segment: &Vec<String>) {
151+
let len = segment.len();
152+
if len <= 1 || segment[0] != self.prefix {
153+
return;
154+
}
155+
let mut find = false;
156+
let child_len = self.child.borrow().len();
157+
let mut child_segment = segment.clone();
158+
child_segment.remove(0);
159+
let find_path = child_segment[0].clone();
160+
for j in 0..child_len {
161+
if self.child.borrow()[j].prefix == find_path {
162+
find = true;
163+
self.child.borrow_mut()[j].add_child(&child_segment);
164+
break;
165+
}
166+
}
167+
if !find {
168+
let mut child_node = MakeSuggestionNode::new(find_path);
169+
child_node.add_child(&child_segment);
170+
self.child.borrow_mut().push(child_node);
171+
}
172+
}
173+
174+
fn make_suggestion(&self) -> String {
175+
let mut str = self.prefix.clone();
176+
if self.child.borrow().len() > 0 {
177+
str.push_str("::");
178+
let mut child_import = self.child.borrow().iter().fold("".to_string(), |mut acc, x| {
179+
let temp = x.make_suggestion();
180+
acc.push_str(&*temp);
181+
acc.push_str(", ");
182+
acc
183+
});
184+
child_import = child_import[0..child_import.len() - 2].to_string();
185+
if self.child.borrow().len() > 1 {
186+
str.push_str("{");
187+
str.push_str(&*child_import);
188+
str.push_str("}");
189+
} else {
190+
str.push_str(&*child_import);
191+
}
192+
}
193+
str
194+
}
195+
}
196+
197+
#[derive(Debug)]
198+
struct MakeSuggestionTree {
199+
root: MakeSuggestionNode,
200+
}
201+
202+
impl MakeSuggestionTree {
203+
fn new(node: MakeSuggestionNode) -> Self {
204+
MakeSuggestionTree { root: node }
205+
}
206+
207+
fn add_node(&mut self, segment: &Vec<String>) {
208+
self.root.add_child(segment);
209+
}
210+
211+
fn make_suggestion(&self) -> String {
212+
self.root.make_suggestion()
213+
}
214+
}
215+
216+
impl RedundantPreludeImports {
217+
fn check_use_tree(
218+
&self,
219+
cx: &EarlyContext<'_>,
220+
use_tree: &ast::UseTree,
221+
pre_path: &Vec<String>,
222+
redundant: &mut RedundantImport<'_, '_>,
223+
) {
224+
let mut pre_path = pre_path.clone();
225+
use_tree.prefix.segments.iter().for_each(|p| {
226+
pre_path.push(p.ident.to_string());
227+
});
228+
match use_tree.kind {
229+
ast::UseTreeKind::Nested(ref items) => {
230+
for (tree, _) in items {
231+
// Recursive process nested usetree.
232+
self.check_use_tree(cx, tree, &pre_path, redundant);
233+
}
234+
}
235+
ast::UseTreeKind::Simple(None) => {
236+
let path = pre_path.iter().fold("".to_string(), |mut acc, x| {
237+
acc.push_str(&*x);
238+
acc.push_str("::");
239+
acc
240+
});
241+
if let Some(val) = redundant.prelude_imports.get(&path[0..path.len()-2])
242+
&& (*val == sym::rust_2015
243+
|| (*val == sym::rust_2018 && redundant.item.span.at_least_rust_2018())
244+
|| (*val == sym::rust_2021 && redundant.item.span.at_least_rust_2021())
245+
|| (*val == sym::rust_2024 && redundant.item.span.at_least_rust_2024())) {
246+
redundant.remove_imports.push((use_tree.span, pre_path.clone()));
247+
} else {
248+
redundant.reserve_imports.push((use_tree.span, pre_path));
249+
}
250+
}
251+
_ => {}
252+
}
253+
}
254+
}

compiler/rustc_resolve/src/build_reduced_graph.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
1212
use crate::{errors, BindingKey, MacroData, NameBindingData};
1313
use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot};
1414
use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError};
15-
use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError};
15+
use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError};
1616

1717
use rustc_ast::visit::{self, AssocCtxt, Visitor};
1818
use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
@@ -358,7 +358,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
358358
root_span,
359359
root_id,
360360
vis: Cell::new(Some(vis)),
361-
used: Cell::new(false),
361+
used: Default::default(),
362362
});
363363

364364
self.r.indeterminate_imports.push(import);
@@ -852,7 +852,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
852852
span: item.span,
853853
module_path: Vec::new(),
854854
vis: Cell::new(Some(vis)),
855-
used: Cell::new(used),
855+
used: Cell::new(used.then_some(Used::Other)),
856856
});
857857
self.r.potentially_unused_imports.push(import);
858858
let imported_binding = self.r.import(binding, import);
@@ -1061,7 +1061,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
10611061
span,
10621062
module_path: Vec::new(),
10631063
vis: Cell::new(Some(ty::Visibility::Restricted(CRATE_DEF_ID))),
1064-
used: Cell::new(false),
1064+
used: Default::default(),
10651065
})
10661066
};
10671067

@@ -1225,7 +1225,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
12251225
span,
12261226
module_path: Vec::new(),
12271227
vis: Cell::new(Some(vis)),
1228-
used: Cell::new(true),
1228+
used: Cell::new(Some(Used::Other)),
12291229
});
12301230
let import_binding = self.r.import(binding, import);
12311231
self.r.define(self.r.graph_root, ident, MacroNS, import_binding);

compiler/rustc_resolve/src/check_unused.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,26 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
182182
}
183183
} else {
184184
self.check_import(id);
185+
let import = self.r.redundant_imports.remove(&id);
186+
if let Some(import) = import {
187+
if let ImportKind::Single {
188+
source,
189+
target,
190+
ref source_bindings,
191+
ref target_bindings,
192+
..
193+
} = import.kind
194+
&& !self.unused_imports.contains_key(&self.base_id)
195+
{
196+
self.r.check_for_redundant_imports(
197+
source,
198+
import,
199+
source_bindings,
200+
target_bindings,
201+
target,
202+
);
203+
}
204+
}
185205
}
186206

187207
visit::walk_use_tree(self, use_tree, id);
@@ -286,7 +306,7 @@ impl Resolver<'_, '_> {
286306

287307
for import in self.potentially_unused_imports.iter() {
288308
match import.kind {
289-
_ if import.used.get()
309+
_ if import.used.get().is_some()
290310
|| import.expect_vis().is_public()
291311
|| import.span.is_dummy() =>
292312
{

compiler/rustc_resolve/src/diagnostics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ use crate::errors::{
3333
};
3434
use crate::imports::{Import, ImportKind};
3535
use crate::late::{PatternSource, Rib};
36-
use crate::path_names_to_string;
3736
use crate::{errors as errs, BindingKey};
37+
use crate::{path_names_to_string, Used};
3838
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, Finalize};
3939
use crate::{HasGenericParams, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot};
4040
use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, VisResolutionError};
@@ -1482,7 +1482,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
14821482
);
14831483
// Silence the 'unused import' warning we might get,
14841484
// since this diagnostic already covers that import.
1485-
self.record_use(ident, binding, false);
1485+
self.record_use(ident, binding, Some(Used::Other));
14861486
return;
14871487
}
14881488
}

0 commit comments

Comments
 (0)