1 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
2 use rustc_ast::node_id::{NodeId, NodeMap};
3 use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
4 use rustc_errors::Applicability;
5 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
6 use rustc_session::{declare_tool_lint, impl_lint_pass};
7 use rustc_span::{edition::Edition, symbol::kw, Span, Symbol};
11 /// Checking for imports with single component use path.
13 /// ### Why is this bad?
14 /// Import with single component use path such as `use cratename;`
15 /// is not necessary, and thus should be removed.
22 /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
28 /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
31 #[clippy::version = "1.43.0"]
32 pub SINGLE_COMPONENT_PATH_IMPORTS,
34 "imports with single component path are redundant"
38 pub struct SingleComponentPathImports {
39 /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected
40 found: NodeMap<Vec<SingleUse>>,
50 impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
52 impl EarlyLintPass for SingleComponentPathImports {
53 fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
54 if cx.sess().opts.edition < Edition::Edition2018 {
58 self.check_mod(cx, &krate.items);
61 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
62 for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() {
66 SINGLE_COMPONENT_PATH_IMPORTS,
68 "this import is redundant",
71 Applicability::MachineApplicable,
76 SINGLE_COMPONENT_PATH_IMPORTS,
78 "this import is redundant",
87 impl SingleComponentPathImports {
88 fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P<Item>]) {
89 // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example
90 // below. Removing the `use crypto_hash;` would make this a compile error
94 // use self::crypto_hash::{Algorithm, Hasher};
96 let mut imports_reused_with_self = Vec::new();
98 // keep track of single use statements such as `crypto_hash` in the example below
102 let mut single_use_usages = Vec::new();
104 // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
106 // macro_rules! foo { () => {} };
107 // pub(crate) use foo;
109 let mut macros = Vec::new();
115 &mut imports_reused_with_self,
116 &mut single_use_usages,
121 for usage in single_use_usages {
122 if !imports_reused_with_self.contains(&usage.name) {
123 self.found.entry(usage.item_id).or_default().push(usage);
130 cx: &EarlyContext<'_>,
132 imports_reused_with_self: &mut Vec<Symbol>,
133 single_use_usages: &mut Vec<SingleUse>,
134 macros: &mut Vec<Symbol>,
136 if item.span.from_expansion() || item.vis.kind.is_pub() {
141 ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
142 self.check_mod(cx, items);
144 ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
145 macros.push(item.ident.name);
147 ItemKind::Use(use_tree) => {
148 let segments = &use_tree.prefix.segments;
150 // keep track of `use some_module;` usages
151 if segments.len() == 1 {
152 if let UseTreeKind::Simple(None) = use_tree.kind {
153 let name = segments[0].ident.name;
154 if !macros.contains(&name) {
155 single_use_usages.push(SingleUse {
166 if segments.is_empty() {
167 // keep track of `use {some_module, some_other_module};` usages
168 if let UseTreeKind::Nested(trees) = &use_tree.kind {
170 let segments = &tree.0.prefix.segments;
171 if segments.len() == 1 {
172 if let UseTreeKind::Simple(None) = tree.0.kind {
173 let name = segments[0].ident.name;
174 if !macros.contains(&name) {
175 single_use_usages.push(SingleUse {
187 // keep track of `use self::some_module` usages
188 if segments[0].ident.name == kw::SelfLower {
189 // simple case such as `use self::module::SomeStruct`
190 if segments.len() > 1 {
191 imports_reused_with_self.push(segments[1].ident.name);
195 // nested case such as `use self::{module1::Struct1, module2::Struct2}`
196 if let UseTreeKind::Nested(trees) = &use_tree.kind {
198 let segments = &tree.0.prefix.segments;
199 if !segments.is_empty() {
200 imports_reused_with_self.push(segments[0].ident.name);