Skip to content

Commit

Permalink
add lint redundant_prelude_imports:
Browse files Browse the repository at this point in the history
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
surechen committed Nov 10, 2023
1 parent 42e1e12 commit 430987d
Show file tree
Hide file tree
Showing 10 changed files with 365 additions and 0 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,10 @@ lint_reason_must_be_string_literal = reason must be a string literal
lint_reason_must_come_last = reason in lint attribute must come last
lint_redundant_prelude_imports = unnecessary prelude import {$redundant_crates}
.suggestion = remove unused imports
.help = change to {$replace}
lint_redundant_semicolons =
unnecessary trailing {$multiple ->
[true] semicolons
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ mod opaque_hidden_inferred_bound;
mod pass_by_value;
mod passes;
mod ptr_nulls;
mod redundant_prelude_imports;
mod redundant_semicolon;
mod reference_casting;
mod traits;
Expand Down Expand Up @@ -119,6 +120,7 @@ use noop_method_call::*;
use opaque_hidden_inferred_bound::*;
use pass_by_value::*;
use ptr_nulls::*;
use redundant_prelude_imports::*;
use redundant_semicolon::*;
use reference_casting::*;
use traits::*;
Expand Down Expand Up @@ -178,6 +180,7 @@ early_lint_methods!(
HiddenUnicodeCodepoints: HiddenUnicodeCodepoints,
IncompleteInternalFeatures: IncompleteInternalFeatures,
RedundantSemicolons: RedundantSemicolons,
RedundantPreludeImports: RedundantPreludeImports,
UnusedDocComment: UnusedDocComment,
UnexpectedCfgs: UnexpectedCfgs,
]
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,16 @@ pub struct RedundantSemicolonsDiag {
pub suggestion: Span,
}

// redundant_prelude_imports.rs
#[derive(LintDiagnostic)]
#[diag(lint_redundant_prelude_imports)]
pub struct RedundantPreludeImportsDiag {
pub redundant_crates: String,
#[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
pub suggestion: Span,
pub replace: String,
}

// traits.rs
pub struct DropTraitConstraintsDiag<'a> {
pub predicate: Clause<'a>,
Expand Down
254 changes: 254 additions & 0 deletions compiler/rustc_lint/src/redundant_prelude_imports.rs
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
/// use std::mem::drop;
/// use std::convert::{TryFrom, TryInto};
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The items in std::prelude is unnecessary to import, so
/// remove those Use items.
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));
}
}
_ => {}
}
}
}
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 tests/ui/lint/use-redundant/redundant_prelude_imports-rust-2015.rs
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() {
}
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

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 tests/ui/lint/use-redundant/redundant_prelude_imports-rust-2018.rs
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() {
}

0 comments on commit 430987d

Please sign in to comment.