Skip to content

Commit 430987d

Browse files
committed
add lint 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}; ``` Replace to: ```rust use std::{option::{Iter, IterMut, IntoIter}, convert::{TryFrom, TryInto}}; ```
1 parent 42e1e12 commit 430987d

10 files changed

+365
-0
lines changed

compiler/rustc_lint/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,10 @@ lint_reason_must_be_string_literal = reason must be a string literal
476476
477477
lint_reason_must_come_last = reason in lint attribute must come last
478478
479+
lint_redundant_prelude_imports = unnecessary prelude import {$redundant_crates}
480+
.suggestion = remove unused imports
481+
.help = change to {$replace}
482+
479483
lint_redundant_semicolons =
480484
unnecessary trailing {$multiple ->
481485
[true] semicolons

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ mod opaque_hidden_inferred_bound;
8181
mod pass_by_value;
8282
mod passes;
8383
mod ptr_nulls;
84+
mod redundant_prelude_imports;
8485
mod redundant_semicolon;
8586
mod reference_casting;
8687
mod traits;
@@ -119,6 +120,7 @@ use noop_method_call::*;
119120
use opaque_hidden_inferred_bound::*;
120121
use pass_by_value::*;
121122
use ptr_nulls::*;
123+
use redundant_prelude_imports::*;
122124
use redundant_semicolon::*;
123125
use reference_casting::*;
124126
use traits::*;
@@ -178,6 +180,7 @@ early_lint_methods!(
178180
HiddenUnicodeCodepoints: HiddenUnicodeCodepoints,
179181
IncompleteInternalFeatures: IncompleteInternalFeatures,
180182
RedundantSemicolons: RedundantSemicolons,
183+
RedundantPreludeImports: RedundantPreludeImports,
181184
UnusedDocComment: UnusedDocComment,
182185
UnexpectedCfgs: UnexpectedCfgs,
183186
]

compiler/rustc_lint/src/lints.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,16 @@ pub struct RedundantSemicolonsDiag {
13601360
pub suggestion: Span,
13611361
}
13621362

1363+
// redundant_prelude_imports.rs
1364+
#[derive(LintDiagnostic)]
1365+
#[diag(lint_redundant_prelude_imports)]
1366+
pub struct RedundantPreludeImportsDiag {
1367+
pub redundant_crates: String,
1368+
#[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
1369+
pub suggestion: Span,
1370+
pub replace: String,
1371+
}
1372+
13631373
// traits.rs
13641374
pub struct DropTraitConstraintsDiag<'a> {
13651375
pub predicate: Clause<'a>,
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+
/// use std::mem::drop;
18+
/// use std::convert::{TryFrom, TryInto};
19+
/// ```
20+
///
21+
/// {{produces}}
22+
///
23+
/// ### Explanation
24+
///
25+
/// The items in std::prelude is unnecessary to import, so
26+
/// remove those Use items.
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
// check-pass
3+
#![allow(unused_imports)]
4+
#![warn(redundant_prelude_imports)]
5+
6+
7+
//~^ WARN unnecessary prelude import 'self' 'Some' 'None'
8+
9+
//~^ WARN unnecessary prelude import 'Ok' 'Err'
10+
use std::convert::{TryFrom, TryInto};
11+
12+
fn main() {
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
// check-pass
3+
#![allow(unused_imports)]
4+
#![warn(redundant_prelude_imports)]
5+
6+
use std::option::Option::{self, Some, None};
7+
//~^ WARN unnecessary prelude import 'self' 'Some' 'None'
8+
use std::result::Result::{Ok, Err};
9+
//~^ WARN unnecessary prelude import 'Ok' 'Err'
10+
use std::convert::{TryFrom, TryInto};
11+
12+
fn main() {
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
warning: unnecessary prelude import 'self' 'Some' 'None'
2+
--> $DIR/redundant_prelude_imports-rust-2015.rs:6:1
3+
|
4+
LL | use std::option::Option::{self, Some, None};
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove unused imports
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/redundant_prelude_imports-rust-2015.rs:4:9
9+
|
10+
LL | #![warn(redundant_prelude_imports)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
warning: unnecessary prelude import 'Ok' 'Err'
14+
--> $DIR/redundant_prelude_imports-rust-2015.rs:8:1
15+
|
16+
LL | use std::result::Result::{Ok, Err};
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove unused imports
18+
19+
warning: 2 warnings emitted
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
// check-pass
3+
// edition:2018
4+
5+
#![allow(unused_imports)]
6+
#![warn(redundant_prelude_imports)]
7+
8+
9+
//~^ WARN unnecessary prelude import 'Ok' 'Err'
10+
11+
//~^ WARN unnecessary prelude import 'TryFrom' 'TryInto'
12+
13+
fn main() {
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
// check-pass
3+
// edition:2018
4+
5+
#![allow(unused_imports)]
6+
#![warn(redundant_prelude_imports)]
7+
8+
use std::result::Result::{Ok, Err};
9+
//~^ WARN unnecessary prelude import 'Ok' 'Err'
10+
use std::convert::{TryFrom, TryInto};
11+
//~^ WARN unnecessary prelude import 'TryFrom' 'TryInto'
12+
13+
fn main() {
14+
}

0 commit comments

Comments
 (0)