1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::is_test_module_or_function;
3 use clippy_utils::source::{snippet, snippet_with_applicability};
4 use if_chain::if_chain;
5 use rustc_errors::Applicability;
8 Item, ItemKind, PathSegment, UseKind,
10 use rustc_lint::{LateContext, LateLintPass};
12 use rustc_session::{declare_tool_lint, impl_lint_pass};
13 use rustc_span::symbol::kw;
14 use rustc_span::{sym, BytePos};
16 declare_clippy_lint! {
18 /// Checks for `use Enum::*`.
20 /// ### Why is this bad?
21 /// It is usually better style to use the prefixed name of
22 /// an enumeration variant, rather than importing variants.
24 /// ### Known problems
25 /// Old-style enumerations that prefix the variants are
31 /// use std::cmp::Ordering::*;
35 /// use std::cmp::Ordering;
36 /// foo(Ordering::Less)
38 #[clippy::version = "pre 1.29.0"]
41 "use items that import all variants of an enum"
44 declare_clippy_lint! {
46 /// Checks for wildcard imports `use _::*`.
48 /// ### Why is this bad?
49 /// wildcard imports can pollute the namespace. This is especially bad if
50 /// you try to import something through a wildcard, that already has been imported by name from
51 /// a different source:
54 /// use crate1::foo; // Imports a function named foo
55 /// use crate2::*; // Has a function named foo
57 /// foo(); // Calls crate1::foo
60 /// This can lead to confusing error messages at best and to unexpected behavior at worst.
63 /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)
64 /// provide modules named "prelude" specifically designed for wildcard import.
66 /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
68 /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.
70 /// ### Known problems
71 /// If macros are imported through the wildcard, this macro is not included
72 /// by the suggestion and has to be added by hand.
74 /// Applying the suggestion when explicit imports of the things imported with a glob import
75 /// exist, may result in `unused_imports` warnings.
91 #[clippy::version = "1.43.0"]
94 "lint `use _::*` statements"
98 pub struct WildcardImports {
100 test_modules_deep: u32,
103 impl WildcardImports {
104 pub fn new(warn_on_all: bool) -> Self {
107 test_modules_deep: 0,
112 impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
114 impl LateLintPass<'_> for WildcardImports {
115 fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
116 if is_test_module_or_function(cx.tcx, item) {
117 self.test_modules_deep = self.test_modules_deep.saturating_add(1);
119 let module = cx.tcx.parent_module_from_def_id(item.def_id);
120 if cx.tcx.visibility(item.def_id) != ty::Visibility::Restricted(module.to_def_id()) {
124 if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
125 if self.warn_on_all || !self.check_exceptions(item, use_path.segments);
126 let used_imports = cx.tcx.names_imported_by_glob_use(item.def_id);
127 if !used_imports.is_empty(); // Already handled by `unused_imports`
129 let mut applicability = Applicability::MachineApplicable;
130 let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
131 let (span, braced_glob) = if import_source_snippet.is_empty() {
132 // This is a `_::{_, *}` import
133 // In this case `use_path.span` is empty and ends directly in front of the `*`,
134 // so we need to extend it by one byte.
136 use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
140 // In this case, the `use_path.span` ends right before the `::*`, so we need to
141 // extend it up to the `*`. Since it is hard to find the `*` in weird
142 // formattings like `use _ :: *;`, we extend it up to, but not including the
143 // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
144 // can just use the end of the item span
145 let mut span = use_path.span.with_hi(item.span.hi());
146 if snippet(cx, span, "").ends_with(';') {
147 span = use_path.span.with_hi(item.span.hi() - BytePos(1));
154 let imports_string = if used_imports.len() == 1 {
155 used_imports.iter().next().unwrap().to_string()
157 let mut imports = used_imports
159 .map(ToString::to_string)
160 .collect::<Vec<_>>();
165 format!("{{{}}}", imports.join(", "))
169 let sugg = if braced_glob {
172 format!("{}::{}", import_source_snippet, imports_string)
175 let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
176 (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
178 (WILDCARD_IMPORTS, "usage of wildcard import")
194 fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
195 if is_test_module_or_function(cx.tcx, item) {
196 self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
201 impl WildcardImports {
202 fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
203 item.span.from_expansion()
204 || is_prelude_import(segments)
205 || (is_super_only_import(segments) && self.test_modules_deep > 0)
209 // Allow "...prelude::..::*" imports.
210 // Many crates have a prelude, and it is imported as a glob by design.
211 fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
212 segments.iter().any(|ps| ps.ident.name == sym::prelude)
215 // Allow "super::*" imports in tests.
216 fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
217 segments.len() == 1 && segments[0].ident.name == kw::Super