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