]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/wildcard_imports.rs
36f910c983f64d7ebdccb9e41d27c479c4793ba5
[rustc.git] / src / tools / clippy / clippy_lints / src / wildcard_imports.rs
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;
6 use rustc_hir::{
7 def::{DefKind, Res},
8 Item, ItemKind, PathSegment, UseKind,
9 };
10 use rustc_lint::{LateContext, LateLintPass};
11 use rustc_middle::ty;
12 use rustc_session::{declare_tool_lint, impl_lint_pass};
13 use rustc_span::symbol::kw;
14 use rustc_span::{sym, BytePos};
15
16 declare_clippy_lint! {
17 /// ### What it does
18 /// Checks for `use Enum::*`.
19 ///
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.
23 ///
24 /// ### Known problems
25 /// Old-style enumerations that prefix the variants are
26 /// still around.
27 ///
28 /// ### Example
29 /// ```rust
30 /// use std::cmp::Ordering::*;
31 ///
32 /// # fn foo(_: std::cmp::Ordering) {}
33 /// foo(Less);
34 /// ```
35 ///
36 /// Use instead:
37 /// ```rust
38 /// use std::cmp::Ordering;
39 ///
40 /// # fn foo(_: Ordering) {}
41 /// foo(Ordering::Less)
42 /// ```
43 #[clippy::version = "pre 1.29.0"]
44 pub ENUM_GLOB_USE,
45 pedantic,
46 "use items that import all variants of an enum"
47 }
48
49 declare_clippy_lint! {
50 /// ### What it does
51 /// Checks for wildcard imports `use _::*`.
52 ///
53 /// ### Why is this bad?
54 /// wildcard imports can pollute the namespace. This is especially bad if
55 /// you try to import something through a wildcard, that already has been imported by name from
56 /// a different source:
57 ///
58 /// ```rust,ignore
59 /// use crate1::foo; // Imports a function named foo
60 /// use crate2::*; // Has a function named foo
61 ///
62 /// foo(); // Calls crate1::foo
63 /// ```
64 ///
65 /// This can lead to confusing error messages at best and to unexpected behavior at worst.
66 ///
67 /// ### Exceptions
68 /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)
69 /// provide modules named "prelude" specifically designed for wildcard import.
70 ///
71 /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
72 ///
73 /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.
74 ///
75 /// ### Known problems
76 /// If macros are imported through the wildcard, this macro is not included
77 /// by the suggestion and has to be added by hand.
78 ///
79 /// Applying the suggestion when explicit imports of the things imported with a glob import
80 /// exist, may result in `unused_imports` warnings.
81 ///
82 /// ### Example
83 /// ```rust,ignore
84 /// use crate1::*;
85 ///
86 /// foo();
87 /// ```
88 ///
89 /// Use instead:
90 /// ```rust,ignore
91 /// use crate1::foo;
92 ///
93 /// foo();
94 /// ```
95 #[clippy::version = "1.43.0"]
96 pub WILDCARD_IMPORTS,
97 pedantic,
98 "lint `use _::*` statements"
99 }
100
101 #[derive(Default)]
102 pub struct WildcardImports {
103 warn_on_all: bool,
104 test_modules_deep: u32,
105 }
106
107 impl WildcardImports {
108 pub fn new(warn_on_all: bool) -> Self {
109 Self {
110 warn_on_all,
111 test_modules_deep: 0,
112 }
113 }
114 }
115
116 impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
117
118 impl LateLintPass<'_> for WildcardImports {
119 fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
120 if is_test_module_or_function(cx.tcx, item) {
121 self.test_modules_deep = self.test_modules_deep.saturating_add(1);
122 }
123 let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id);
124 if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) {
125 return;
126 }
127 if_chain! {
128 if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
129 if self.warn_on_all || !self.check_exceptions(item, use_path.segments);
130 let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id);
131 if !used_imports.is_empty(); // Already handled by `unused_imports`
132 then {
133 let mut applicability = Applicability::MachineApplicable;
134 let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
135 let (span, braced_glob) = if import_source_snippet.is_empty() {
136 // This is a `_::{_, *}` import
137 // In this case `use_path.span` is empty and ends directly in front of the `*`,
138 // so we need to extend it by one byte.
139 (
140 use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
141 true,
142 )
143 } else {
144 // In this case, the `use_path.span` ends right before the `::*`, so we need to
145 // extend it up to the `*`. Since it is hard to find the `*` in weird
146 // formattings like `use _ :: *;`, we extend it up to, but not including the
147 // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
148 // can just use the end of the item span
149 let mut span = use_path.span.with_hi(item.span.hi());
150 if snippet(cx, span, "").ends_with(';') {
151 span = use_path.span.with_hi(item.span.hi() - BytePos(1));
152 }
153 (
154 span, false,
155 )
156 };
157
158 let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false);
159 let imports_string = if imports.len() == 1 {
160 imports.pop().unwrap()
161 } else if braced_glob {
162 imports.join(", ")
163 } else {
164 format!("{{{}}}", imports.join(", "))
165 };
166
167 let sugg = if braced_glob {
168 imports_string
169 } else {
170 format!("{import_source_snippet}::{imports_string}")
171 };
172
173 // Glob imports always have a single resolution.
174 let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] {
175 (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
176 } else {
177 (WILDCARD_IMPORTS, "usage of wildcard import")
178 };
179
180 span_lint_and_sugg(
181 cx,
182 lint,
183 span,
184 message,
185 "try",
186 sugg,
187 applicability,
188 );
189 }
190 }
191 }
192
193 fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
194 if is_test_module_or_function(cx.tcx, item) {
195 self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
196 }
197 }
198 }
199
200 impl WildcardImports {
201 fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
202 item.span.from_expansion()
203 || is_prelude_import(segments)
204 || (is_super_only_import(segments) && self.test_modules_deep > 0)
205 }
206 }
207
208 // Allow "...prelude::..::*" imports.
209 // Many crates have a prelude, and it is imported as a glob by design.
210 fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
211 segments.iter().any(|ps| ps.ident.name == sym::prelude)
212 }
213
214 // Allow "super::*" imports in tests.
215 fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
216 segments.len() == 1 && segments[0].ident.name == kw::Super
217 }