Skip to content

Commit 76db21e

Browse files
committed
add unnecessary_self_imports lint
1 parent e9728b8 commit 76db21e

File tree

7 files changed

+282
-1
lines changed

7 files changed

+282
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2519,6 +2519,7 @@ Released 2018-09-13
25192519
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
25202520
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
25212521
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
2522+
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
25222523
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
25232524
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
25242525
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps

clippy_lints/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ mod unicode;
356356
mod unit_return_expecting_ord;
357357
mod unit_types;
358358
mod unnamed_address;
359+
mod unnecessary_self_imports;
359360
mod unnecessary_sort_by;
360361
mod unnecessary_wraps;
361362
mod unnested_or_patterns;
@@ -963,6 +964,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
963964
unit_types::UNIT_CMP,
964965
unnamed_address::FN_ADDRESS_COMPARISONS,
965966
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
967+
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
966968
unnecessary_sort_by::UNNECESSARY_SORT_BY,
967969
unnecessary_wraps::UNNECESSARY_WRAPS,
968970
unnested_or_patterns::UNNESTED_OR_PATTERNS,
@@ -1048,6 +1050,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10481050
store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
10491051
store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor);
10501052
store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions);
1053+
store.register_late_pass(|| box unnecessary_self_imports::UnnecessarySelfImports);
10511054

10521055
let msrv = conf.msrv.as_ref().and_then(|s| {
10531056
parse_msrv(s, None, None).or_else(|| {
@@ -1708,6 +1711,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
17081711
LintId::of(unit_types::UNIT_CMP),
17091712
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
17101713
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
1714+
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
17111715
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
17121716
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
17131717
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
@@ -1832,6 +1836,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
18321836
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
18331837
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
18341838
LintId::of(try_err::TRY_ERR),
1839+
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
18351840
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
18361841
LintId::of(unused_unit::UNUSED_UNIT),
18371842
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),

clippy_lints/src/modulo_arithmetic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use clippy_utils::sext;
44
use if_chain::if_chain;
55
use rustc_hir::{BinOpKind, Expr, ExprKind};
66
use rustc_lint::{LateContext, LateLintPass};
7-
use rustc_middle::ty::{self};
7+
use rustc_middle::ty;
88
use rustc_session::{declare_lint_pass, declare_tool_lint};
99
use std::fmt::Display;
1010

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::in_macro;
3+
use clippy_utils::source::snippet_with_applicability;
4+
use if_chain::if_chain;
5+
use rustc_errors::Applicability;
6+
use rustc_hir::def::{DefKind, Res};
7+
use rustc_hir::{Item, ItemKind, Mod, Node, Path, UseKind, VisibilityKind};
8+
use rustc_lint::{LateContext, LateLintPass};
9+
use rustc_session::{declare_lint_pass, declare_tool_lint};
10+
use rustc_span::def_id::DefId;
11+
use rustc_span::symbol::Ident;
12+
use rustc_span::{BytePos, Span};
13+
14+
declare_clippy_lint! {
15+
/// **What it does:** Checks for imports ending in `::{self};`.
16+
///
17+
/// **Why is this bad?** In most cases, this can be written much more cleanly by omitting `self`.
18+
///
19+
/// **Known problems:** None.
20+
///
21+
/// **Example:**
22+
///
23+
/// ```rust
24+
/// use std::io::{self};
25+
/// ```
26+
/// Use instead:
27+
/// ```rust
28+
/// use std::io;
29+
/// ```
30+
pub UNNECESSARY_SELF_IMPORTS,
31+
style,
32+
"imports ending in `self`, which can be omitted"
33+
}
34+
35+
declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]);
36+
37+
impl<'tcx> LateLintPass<'tcx> for UnnecessarySelfImports {
38+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
39+
if_chain! {
40+
if !in_macro(item.span);
41+
if let ItemKind::Use(path, UseKind::ListStem) = &item.kind;
42+
if let Some(ident) = is_self_import(cx, item, path);
43+
if let Some(Res::Def(DefKind::Mod, def_id)) = path.segments[path.segments.len() - 2].res;
44+
if let Some(last) = path.segments.last();
45+
if !mod_contains_item(cx, def_id, last.ident) || !item_is_in_scope(cx, ident, path.span);
46+
then {
47+
let mut applicability = Applicability::MachineApplicable;
48+
let snippet = if ident == path.segments[path.segments.len() - 1].ident {
49+
let adjusted_span = path.span.with_hi(path.span.hi() - BytePos(8));
50+
format!(
51+
"{}",
52+
snippet_with_applicability(cx, adjusted_span, "..", &mut applicability)
53+
)
54+
} else {
55+
let adjusted_span = path.span.until(ident.span);
56+
let adjusted_span = adjusted_span.with_hi(adjusted_span.hi() - BytePos(11));
57+
format!(
58+
"{} as {}",
59+
snippet_with_applicability(cx, adjusted_span, "..", &mut applicability),
60+
snippet_with_applicability(cx, ident.span, "..", &mut applicability)
61+
)
62+
};
63+
span_lint_and_sugg(
64+
cx,
65+
UNNECESSARY_SELF_IMPORTS,
66+
path.span,
67+
"import ending with `self`",
68+
"consider omitting `::{self}`",
69+
snippet,
70+
applicability,
71+
);
72+
}
73+
}
74+
}
75+
}
76+
77+
fn is_self_import(cx: &LateContext<'_>, current_import: &Item<'_>, current_import_path: &Path<'_>) -> Option<Ident> {
78+
let definitions = cx.tcx.hir().definitions();
79+
let mut amt = 0;
80+
let mut ident: Option<Ident> = None;
81+
82+
for def in definitions.iter_local_def_id() {
83+
if_chain! {
84+
if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def.to_def_id());
85+
if let ItemKind::Use(path, kind) = item.kind;
86+
if matches!(kind, UseKind::Single | UseKind::Glob);
87+
if current_import.span.contains(item.span);
88+
then {
89+
amt += 1;
90+
if amt > 1 { return None; }
91+
92+
if_chain! {
93+
if current_import_path.segments.len() == path.segments.len();
94+
let current_import_last = &current_import_path.segments[current_import_path.segments.len() - 1];
95+
let item_last = &path.segments[path.segments.len() - 1];
96+
if current_import_last.ident == item_last.ident;
97+
then {
98+
ident = Some(item.ident);
99+
}
100+
}
101+
}
102+
}
103+
}
104+
ident
105+
}
106+
107+
fn mod_contains_item(cx: &LateContext<'_>, def_id: DefId, ident: Ident) -> bool {
108+
if let Some(Node::Item(node)) = cx.tcx.hir().get_if_local(def_id) {
109+
if let ItemKind::Mod(Mod { item_ids, .. }) = &node.kind {
110+
for item in item_ids.iter() {
111+
if_chain! {
112+
if let Some(Node::Item(node)) = cx.tcx.hir().get_if_local(item.def_id.to_def_id());
113+
if let VisibilityKind::Public = node.vis.node;
114+
if node.ident == ident;
115+
if matches!(node.kind, ItemKind::Fn(..) | ItemKind::Const(..) | ItemKind::Static(..));
116+
then { return true; }
117+
118+
}
119+
}
120+
}
121+
} else {
122+
for item in cx.tcx.item_children(def_id).iter() {
123+
if_chain! {
124+
if item.ident == ident;
125+
if matches!(item.res, Res::Def(DefKind::Fn | DefKind::Const | DefKind::Static, _));
126+
then { return true; }
127+
}
128+
}
129+
}
130+
false
131+
}
132+
133+
fn item_is_in_scope(cx: &LateContext<'_>, ident: Ident, current_import: Span) -> bool {
134+
let definitions = cx.tcx.hir().definitions();
135+
let mut outer_mod: Option<Span> = None;
136+
137+
for def in definitions.iter_local_def_id() {
138+
if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def.to_def_id()) {
139+
let should_check = match outer_mod {
140+
Some(curr_mod) => {
141+
if curr_mod.contains(item.span) {
142+
false
143+
} else {
144+
outer_mod = None;
145+
true
146+
}
147+
},
148+
None => true,
149+
};
150+
151+
if should_check {
152+
match item.kind {
153+
ItemKind::Fn(..) | ItemKind::Const(..) | ItemKind::Static(..) => {
154+
if item.ident == ident {
155+
return true;
156+
}
157+
},
158+
ItemKind::Use(path, kind) => {
159+
if_chain! {
160+
if let UseKind::Single = kind;
161+
if !current_import.contains(path.span);
162+
if item.ident == ident;
163+
164+
then {
165+
return true;
166+
}
167+
}
168+
},
169+
_ => {
170+
outer_mod = Some(item.span);
171+
},
172+
}
173+
}
174+
}
175+
}
176+
false
177+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run-rustfix
2+
#![warn(clippy::unnecessary_self_imports)]
3+
#![allow(non_camel_case_types, non_upper_case_globals, unused_imports, dead_code)]
4+
5+
mod a {
6+
pub mod mod_and_fn_in_scope {}
7+
pub fn mod_and_fn_in_scope() {}
8+
9+
pub mod only_module {}
10+
11+
pub mod mod_and_fn {}
12+
pub fn mod_and_fn() {}
13+
14+
pub enum enum_and_const {}
15+
pub const enum_and_const: u32 = 1;
16+
17+
pub struct struct_and_static {}
18+
pub static struct_and_static: u32 = 1;
19+
}
20+
21+
mod b {
22+
pub static struct_and_static: u32 = 2;
23+
}
24+
25+
fn mod_and_fn_in_scope() {}
26+
const enum_and_const: u32 = 2;
27+
28+
use std::io;
29+
30+
use a::enum_and_const as alias;
31+
use a::mod_and_fn::{self, *};
32+
use a::mod_and_fn_in_scope::{self};
33+
use a::only_module;
34+
use a::struct_and_static::{self};
35+
36+
use b::struct_and_static;
37+
38+
fn main() {}

tests/ui/unnecessary_self_imports.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run-rustfix
2+
#![warn(clippy::unnecessary_self_imports)]
3+
#![allow(non_camel_case_types, non_upper_case_globals, unused_imports, dead_code)]
4+
5+
mod a {
6+
pub mod mod_and_fn_in_scope {}
7+
pub fn mod_and_fn_in_scope() {}
8+
9+
pub mod only_module {}
10+
11+
pub mod mod_and_fn {}
12+
pub fn mod_and_fn() {}
13+
14+
pub enum enum_and_const {}
15+
pub const enum_and_const: u32 = 1;
16+
17+
pub struct struct_and_static {}
18+
pub static struct_and_static: u32 = 1;
19+
}
20+
21+
mod b {
22+
pub static struct_and_static: u32 = 2;
23+
}
24+
25+
fn mod_and_fn_in_scope() {}
26+
const enum_and_const: u32 = 2;
27+
28+
use std::io::{self};
29+
30+
use a::enum_and_const::{self as alias};
31+
use a::mod_and_fn::{self, *};
32+
use a::mod_and_fn_in_scope::{self};
33+
use a::only_module::{self};
34+
use a::struct_and_static::{self};
35+
36+
use b::struct_and_static;
37+
38+
fn main() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: import ending with `self`
2+
--> $DIR/unnecessary_self_imports.rs:28:5
3+
|
4+
LL | use std::io::{self};
5+
| ^^^^^^^^^^^^^^^ help: consider omitting `::{self}`: `std::io`
6+
|
7+
= note: `-D clippy::unnecessary-self-imports` implied by `-D warnings`
8+
9+
error: import ending with `self`
10+
--> $DIR/unnecessary_self_imports.rs:30:5
11+
|
12+
LL | use a::enum_and_const::{self as alias};
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider omitting `::{self}`: `a::enum_and_const as alias`
14+
15+
error: import ending with `self`
16+
--> $DIR/unnecessary_self_imports.rs:33:5
17+
|
18+
LL | use a::only_module::{self};
19+
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider omitting `::{self}`: `a::only_module`
20+
21+
error: aborting due to 3 previous errors
22+

0 commit comments

Comments
 (0)