X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_lints%2Fsrc%2Fsingle_component_path_imports.rs;h=1eaad438237ec16f4831806b7f5588e527cc151e;hb=ebf88c9734246a790305f6faaba0bf8e3f150dff;hp=5a9bc73fe42b2d78b0b3abcef2cfeecf31fd78c7;hpb=09165ff5761bb1f397e37ee2d32d96adf7afdd86;p=rust.git diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 5a9bc73fe42..1eaad438237 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,10 +1,10 @@ -use crate::utils::{in_macro, span_lint_and_sugg}; -use if_chain::if_chain; +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, MacroDef, ModKind, UseTreeKind, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::edition::Edition; -use syntax::ast::{Item, ItemKind, UseTreeKind}; +use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { /// **What it does:** Checking for imports with single component use path. @@ -16,7 +16,7 @@ /// /// **Example:** /// - /// ```rust, ignore + /// ```rust,ignore /// use regex; /// /// fn main() { @@ -24,7 +24,7 @@ /// } /// ``` /// Better as - /// ```rust, ignore + /// ```rust,ignore /// fn main() { /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); /// } @@ -37,26 +37,144 @@ declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if_chain! { - if !in_macro(item.span); - if cx.sess.opts.edition == Edition::Edition2018; - if !item.vis.node.is_pub(); - if let ItemKind::Use(use_tree) = &item.kind; - if let segments = &use_tree.prefix.segments; - if segments.len() == 1; - if let UseTreeKind::Simple(None, _, _) = use_tree.kind; - then { + 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(); + + // keep track of macros defined in the module as we don't want it to trigger on this (#7106) + // ```rust,ignore + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + + for item in items { + track_uses( + cx, + item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); + } + + 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, - item.span, + single_use.1, "this import is redundant", "remove it entirely", String::new(), - Applicability::MachineApplicable + 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)>, + macros: &mut Vec, +) { + 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::MacroDef(MacroDef { macro_rules: true, .. }) => { + macros.push(item.ident.name); + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; + + let should_report = + |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited); + + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let name = segments[0].ident.name; + if should_report(&name) { + single_use_usages.push((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 name = segments[0].ident.name; + if should_report(&name) { + single_use_usages.push((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); + } + } + } + } + } + }, + _ => {}, + } +}