use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::in_macro; use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { /// **What it does:** Checking for imports with single component use path. /// /// **Why is this bad?** Import with single component use path such as `use cratename;` /// is not necessary, and thus should be removed. /// /// **Known problems:** None. /// /// **Example:** /// /// ```rust,ignore /// use regex; /// /// fn main() { /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); /// } /// ``` /// Better as /// ```rust,ignore /// fn main() { /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); /// } /// ``` pub SINGLE_COMPONENT_PATH_IMPORTS, style, "imports with single component path are redundant" } declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { if cx.sess.opts.edition < Edition::Edition2018 { return; } check_mod(cx, &krate.items); } } fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { // keep track of imports reused with `self` keyword, // such as `self::crypto_hash` in the example below // ```rust,ignore // use self::crypto_hash::{Algorithm, Hasher}; // ``` let mut imports_reused_with_self = Vec::new(); // keep track of single use statements // such as `crypto_hash` in the example below // ```rust,ignore // use crypto_hash; // ``` let mut single_use_usages = Vec::new(); for item in items { track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages); } for single_use in &single_use_usages { if !imports_reused_with_self.contains(&single_use.0) { let can_suggest = single_use.2; if can_suggest { span_lint_and_sugg( cx, SINGLE_COMPONENT_PATH_IMPORTS, single_use.1, "this import is redundant", "remove it entirely", String::new(), Applicability::MachineApplicable, ); } else { span_lint_and_help( cx, SINGLE_COMPONENT_PATH_IMPORTS, single_use.1, "this import is redundant", None, "remove this import", ); } } } } fn track_uses( cx: &EarlyContext<'_>, item: &Item, imports_reused_with_self: &mut Vec, single_use_usages: &mut Vec<(Symbol, Span, bool)>, ) { if in_macro(item.span) || item.vis.kind.is_pub() { return; } match &item.kind { ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { check_mod(cx, &items); }, ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; // keep track of `use some_module;` usages if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { let ident = &segments[0].ident; single_use_usages.push((ident.name, item.span, true)); } return; } if segments.is_empty() { // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested(trees) = &use_tree.kind { for tree in trees { let segments = &tree.0.prefix.segments; if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = tree.0.kind { let ident = &segments[0].ident; single_use_usages.push((ident.name, tree.0.span, false)); } } } } } else { // keep track of `use self::some_module` usages if segments[0].ident.name == kw::SelfLower { // simple case such as `use self::module::SomeStruct` if segments.len() > 1 { imports_reused_with_self.push(segments[1].ident.name); return; } // nested case such as `use self::{module1::Struct1, module2::Struct2}` if let UseTreeKind::Nested(trees) = &use_tree.kind { for tree in trees { let segments = &tree.0.prefix.segments; if !segments.is_empty() { imports_reused_with_self.push(segments[0].ident.name); } } } } } }, _ => {}, } }