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