-
Notifications
You must be signed in to change notification settings - Fork 13k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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}}; ```
- Loading branch information
Showing
10 changed files
with
365 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
use crate::{lints::RedundantPreludeImportsDiag, EarlyContext, EarlyLintPass, LintContext}; | ||
use rustc_ast as ast; | ||
use rustc_span::symbol::{sym, Symbol}; | ||
use rustc_span::Span; | ||
use std::cell::RefCell; | ||
use std::collections::BTreeMap; | ||
use std::rc::Rc; | ||
use std::string::ToString; | ||
|
||
declare_lint! { | ||
/// The `redundant_prelude_imports` lint detects unnecessary imports in | ||
/// std::prelude that can be eliminated. | ||
/// | ||
/// ### Example | ||
/// | ||
/// ```rust | ||
/// # #![allow(unused)] | ||
/// # #![warn(redundant_prelude_imports)] | ||
/// use std::mem::drop; | ||
/// ``` | ||
/// | ||
/// {{produces}} | ||
/// | ||
/// ### Explanation | ||
/// | ||
/// unnecessary prelude import 'std::mem::drop' | ||
pub(super) REDUNDANT_PRELUDE_IMPORTS, | ||
Allow, | ||
"detects unnecessary imports in std::prelude that can be eliminated" | ||
} | ||
|
||
declare_lint_pass!(RedundantPreludeImports => [REDUNDANT_PRELUDE_IMPORTS]); | ||
|
||
#[derive(Debug)] | ||
struct RedundantImport<'a, 'b> { | ||
item: &'a ast::Item, | ||
prelude_imports: &'b BTreeMap<&'b str, Symbol>, | ||
remove_imports: Vec<(Span, Vec<String>)>, | ||
reserve_imports: Vec<(Span, Vec<String>)>, | ||
} | ||
|
||
impl EarlyLintPass for RedundantPreludeImports { | ||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { | ||
if let ast::ItemKind::Use(ref use_tree) = item.kind { | ||
// Use in std::prelude | ||
let prelude_imports: BTreeMap<&str, Symbol> = BTreeMap::from([ | ||
("std::marker::Copy", sym::rust_2015), | ||
("std::marker::Send", sym::rust_2015), | ||
("std::marker::Sized", sym::rust_2015), | ||
("std::marker::Sync", sym::rust_2015), | ||
("std::marker::Unpin", sym::rust_2015), | ||
("std::ops::Drop", sym::rust_2015), | ||
("std::ops::Fn", sym::rust_2015), | ||
("std::ops::FnMut", sym::rust_2015), | ||
("std::ops::FnOnce", sym::rust_2015), | ||
("std::mem::drop", sym::rust_2015), | ||
("std::boxed::Box", sym::rust_2015), | ||
("std::borrow::ToOwned", sym::rust_2015), | ||
("std::clone::Clone", sym::rust_2015), | ||
("std::cmp::PartialEq", sym::rust_2015), | ||
("std::cmp::PartialOrd", sym::rust_2015), | ||
("std::cmp::Eq", sym::rust_2015), | ||
("std::cmp::Ord", sym::rust_2015), | ||
("std::convert::AsRef", sym::rust_2015), | ||
("std::convert::AsMut", sym::rust_2015), | ||
("std::convert::Into", sym::rust_2015), | ||
("std::convert::From", sym::rust_2015), | ||
("std::default::Default", sym::rust_2015), | ||
("std::iter::Iterator", sym::rust_2015), | ||
("std::iter::Extend", sym::rust_2015), | ||
("std::iter::IntoIterator", sym::rust_2015), | ||
("std::iter::DoubleEndedIterator", sym::rust_2015), | ||
("std::iter::ExactSizeIterator", sym::rust_2015), | ||
("std::option::Option::self", sym::rust_2015), | ||
("std::option::Option::Some", sym::rust_2015), | ||
("std::option::Option::None", sym::rust_2015), | ||
("std::result::Result", sym::rust_2015), | ||
("std::result::Result::Ok", sym::rust_2015), | ||
("std::result::Result::Err", sym::rust_2015), | ||
("std::string::String", sym::rust_2015), | ||
("std::string::ToString", sym::rust_2015), | ||
("std::vec::Vec", sym::rust_2015), | ||
("std::convert::TryFrom", sym::rust_2018), | ||
("std::convert::TryInto", sym::rust_2018), | ||
("std::iter::FromIterator", sym::rust_2018), | ||
]); | ||
let mut redundant = RedundantImport { | ||
item: item, | ||
prelude_imports: &prelude_imports, | ||
remove_imports: vec![], | ||
reserve_imports: vec![], | ||
}; | ||
let path = vec![]; | ||
self.check_use_tree(cx, use_tree, &path, &mut redundant); | ||
if redundant.remove_imports.len() > 0 { | ||
// Delete the usetrees imported by std::prelude. | ||
// Use the prefix tree to make suggestion msg for the remaining ones. | ||
// For import : | ||
// use std::{option::{Iter, IterMut, IntoIter, Option::{self, Some}}, | ||
// convert::{TryFrom, TryInto}, mem::drop}; | ||
// Replace to: | ||
// use std::{option::{Iter, IterMut, IntoIter}, convert::{TryFrom, TryInto}}; | ||
let replace = if redundant.reserve_imports.len() == 0 { | ||
"".to_string() | ||
} else { | ||
let mut tree = | ||
MakeSuggestionTree::new(MakeSuggestionNode::new("std".to_string())); | ||
redundant.reserve_imports.iter().for_each(|v| { | ||
tree.add_node(&v.1); | ||
}); | ||
let mut use_replace = "use ".to_string(); | ||
use_replace.push_str(&tree.make_suggestion()); | ||
use_replace.push(';'); | ||
use_replace | ||
}; | ||
let lint_msg = | ||
&redundant.remove_imports.into_iter().fold("".to_string(), |mut acc, x| { | ||
acc.push_str(" '"); | ||
acc.push_str( | ||
&*cx.sess().parse_sess.source_map().span_to_snippet(x.0).unwrap(), | ||
); | ||
acc.push_str("'"); | ||
acc | ||
}); | ||
cx.emit_spanned_lint( | ||
REDUNDANT_PRELUDE_IMPORTS, | ||
item.span, | ||
RedundantPreludeImportsDiag { | ||
redundant_crates: lint_msg.to_string(), | ||
suggestion: item.span, | ||
replace: replace, | ||
}, | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
struct MakeSuggestionNode { | ||
prefix: String, | ||
child: Rc<RefCell<Vec<MakeSuggestionNode>>>, | ||
} | ||
|
||
impl MakeSuggestionNode { | ||
fn new(prefix: String) -> Self { | ||
Self { prefix: prefix, child: Rc::new(RefCell::new(vec![])) } | ||
} | ||
|
||
fn add_child(&mut self, segment: &Vec<String>) { | ||
let len = segment.len(); | ||
if len <= 1 || segment[0] != self.prefix { | ||
return; | ||
} | ||
let mut find = false; | ||
let child_len = self.child.borrow().len(); | ||
let mut child_segment = segment.clone(); | ||
child_segment.remove(0); | ||
let find_path = child_segment[0].clone(); | ||
for j in 0..child_len { | ||
if self.child.borrow()[j].prefix == find_path { | ||
find = true; | ||
self.child.borrow_mut()[j].add_child(&child_segment); | ||
break; | ||
} | ||
} | ||
if !find { | ||
let mut child_node = MakeSuggestionNode::new(find_path); | ||
child_node.add_child(&child_segment); | ||
self.child.borrow_mut().push(child_node); | ||
} | ||
} | ||
|
||
fn make_suggestion(&self) -> String { | ||
let mut str = self.prefix.clone(); | ||
if self.child.borrow().len() > 0 { | ||
str.push_str("::"); | ||
let mut child_import = self.child.borrow().iter().fold("".to_string(), |mut acc, x| { | ||
let temp = x.make_suggestion(); | ||
acc.push_str(&*temp); | ||
acc.push_str(", "); | ||
acc | ||
}); | ||
child_import = child_import[0..child_import.len() - 2].to_string(); | ||
if self.child.borrow().len() > 1 { | ||
str.push_str("{"); | ||
str.push_str(&*child_import); | ||
str.push_str("}"); | ||
} else { | ||
str.push_str(&*child_import); | ||
} | ||
} | ||
str | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
struct MakeSuggestionTree { | ||
root: MakeSuggestionNode, | ||
} | ||
|
||
impl MakeSuggestionTree { | ||
fn new(node: MakeSuggestionNode) -> Self { | ||
MakeSuggestionTree { root: node } | ||
} | ||
|
||
fn add_node(&mut self, segment: &Vec<String>) { | ||
self.root.add_child(segment); | ||
} | ||
|
||
fn make_suggestion(&self) -> String { | ||
self.root.make_suggestion() | ||
} | ||
} | ||
|
||
impl RedundantPreludeImports { | ||
fn check_use_tree( | ||
&self, | ||
cx: &EarlyContext<'_>, | ||
use_tree: &ast::UseTree, | ||
pre_path: &Vec<String>, | ||
redundant: &mut RedundantImport<'_, '_>, | ||
) { | ||
let mut pre_path = pre_path.clone(); | ||
use_tree.prefix.segments.iter().for_each(|p| { | ||
pre_path.push(p.ident.to_string()); | ||
}); | ||
match use_tree.kind { | ||
ast::UseTreeKind::Nested(ref items) => { | ||
for (tree, _) in items { | ||
// Recursive process nested usetree. | ||
self.check_use_tree(cx, tree, &pre_path, redundant); | ||
} | ||
} | ||
ast::UseTreeKind::Simple(None) => { | ||
let path = pre_path.iter().fold("".to_string(), |mut acc, x| { | ||
acc.push_str(&*x); | ||
acc.push_str("::"); | ||
acc | ||
}); | ||
if let Some(val) = redundant.prelude_imports.get(&path[0..path.len()-2]) | ||
&& (*val == sym::rust_2015 | ||
|| (*val == sym::rust_2018 && redundant.item.span.at_least_rust_2018()) | ||
|| (*val == sym::rust_2021 && redundant.item.span.at_least_rust_2021()) | ||
|| (*val == sym::rust_2024 && redundant.item.span.at_least_rust_2024())) { | ||
redundant.remove_imports.push((use_tree.span, pre_path.clone())); | ||
} else { | ||
redundant.reserve_imports.push((use_tree.span, pre_path)); | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
tests/ui/lint/use-redundant/redundant_prelude_imports-rust-2015.fixed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// run-rustfix | ||
// check-pass | ||
#![allow(unused_imports)] | ||
#![warn(redundant_prelude_imports)] | ||
|
||
|
||
//~^ WARN unnecessary prelude import 'self' 'Some' 'None' | ||
|
||
//~^ WARN unnecessary prelude import 'Ok' 'Err' | ||
use std::convert::{TryFrom, TryInto}; | ||
|
||
fn main() { | ||
} |
13 changes: 13 additions & 0 deletions
13
tests/ui/lint/use-redundant/redundant_prelude_imports-rust-2015.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// run-rustfix | ||
// check-pass | ||
#![allow(unused_imports)] | ||
#![warn(redundant_prelude_imports)] | ||
|
||
use std::option::Option::{self, Some, None}; | ||
//~^ WARN unnecessary prelude import 'self' 'Some' 'None' | ||
use std::result::Result::{Ok, Err}; | ||
//~^ WARN unnecessary prelude import 'Ok' 'Err' | ||
use std::convert::{TryFrom, TryInto}; | ||
|
||
fn main() { | ||
} |
20 changes: 20 additions & 0 deletions
20
tests/ui/lint/use-redundant/redundant_prelude_imports-rust-2015.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
warning: unnecessary prelude import 'self' 'Some' 'None' | ||
--> $DIR/redundant_prelude_imports-rust-2015.rs:6:1 | ||
| | ||
LL | use std::option::Option::{self, Some, None}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove unused imports | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/redundant_prelude_imports-rust-2015.rs:4:9 | ||
| | ||
LL | #![warn(redundant_prelude_imports)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
warning: unnecessary prelude import 'Ok' 'Err' | ||
--> $DIR/redundant_prelude_imports-rust-2015.rs:8:1 | ||
| | ||
LL | use std::result::Result::{Ok, Err}; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove unused imports | ||
|
||
warning: 2 warnings emitted | ||
|
14 changes: 14 additions & 0 deletions
14
tests/ui/lint/use-redundant/redundant_prelude_imports-rust-2018.fixed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// run-rustfix | ||
// check-pass | ||
// edition:2018 | ||
|
||
#![allow(unused_imports)] | ||
#![warn(redundant_prelude_imports)] | ||
|
||
|
||
//~^ WARN unnecessary prelude import 'Ok' 'Err' | ||
|
||
//~^ WARN unnecessary prelude import 'TryFrom' 'TryInto' | ||
|
||
fn main() { | ||
} |
14 changes: 14 additions & 0 deletions
14
tests/ui/lint/use-redundant/redundant_prelude_imports-rust-2018.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// run-rustfix | ||
// check-pass | ||
// edition:2018 | ||
|
||
#![allow(unused_imports)] | ||
#![warn(redundant_prelude_imports)] | ||
|
||
use std::result::Result::{Ok, Err}; | ||
//~^ WARN unnecessary prelude import 'Ok' 'Err' | ||
use std::convert::{TryFrom, TryInto}; | ||
//~^ WARN unnecessary prelude import 'TryFrom' 'TryInto' | ||
|
||
fn main() { | ||
} |
Oops, something went wrong.