]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/wildcard_imports.rs
Auto merge of #5029 - flip1995:wildcard_imports, r=phansch
[rust.git] / clippy_lints / src / wildcard_imports.rs
1 use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg};
2 use if_chain::if_chain;
3 use rustc_errors::Applicability;
4 use rustc_hir::{
5     def::{DefKind, Res},
6     Item, ItemKind, UseKind,
7 };
8 use rustc_lint::{LateContext, LateLintPass};
9 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 use rustc_span::BytePos;
11
12 declare_clippy_lint! {
13     /// **What it does:** Checks for `use Enum::*`.
14     ///
15     /// **Why is this bad?** It is usually better style to use the prefixed name of
16     /// an enumeration variant, rather than importing variants.
17     ///
18     /// **Known problems:** Old-style enumerations that prefix the variants are
19     /// still around.
20     ///
21     /// **Example:**
22     /// ```rust
23     /// use std::cmp::Ordering::*;
24     /// ```
25     pub ENUM_GLOB_USE,
26     pedantic,
27     "use items that import all variants of an enum"
28 }
29
30 declare_clippy_lint! {
31     /// **What it does:** Checks for wildcard imports `use _::*`.
32     ///
33     /// **Why is this bad?** wildcard imports can polute the namespace. This is especially bad if
34     /// you try to import something through a wildcard, that already has been imported by name from
35     /// a different source:
36     ///
37     /// ```rust,ignore
38     /// use crate1::foo; // Imports a function named foo
39     /// use crate2::*; // Has a function named foo
40     ///
41     /// foo(); // Calls crate1::foo
42     /// ```
43     ///
44     /// This can lead to confusing error messages at best and to unexpected behavior at worst.
45     ///
46     /// **Known problems:** If macros are imported through the wildcard, this macro is not included
47     /// by the suggestion and has to be added by hand.
48     ///
49     /// Applying the suggestion when explicit imports of the things imported with a glob import
50     /// exist, may result in `unused_imports` warnings.
51     ///
52     /// **Example:**
53     ///
54     /// Bad:
55     /// ```rust,ignore
56     /// use crate1::*;
57     ///
58     /// foo();
59     /// ```
60     ///
61     /// Good:
62     /// ```rust,ignore
63     /// use crate1::foo;
64     ///
65     /// foo();
66     /// ```
67     pub WILDCARD_IMPORTS,
68     pedantic,
69     "lint `use _::*` statements"
70 }
71
72 declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
73
74 impl LateLintPass<'_, '_> for WildcardImports {
75     fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
76         if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
77             return;
78         }
79         if_chain! {
80             if !in_macro(item.span);
81             if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
82             // don't lint prelude glob imports
83             if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude");
84             let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner_def_id());
85             if !used_imports.is_empty(); // Already handled by `unused_imports`
86             then {
87                 let mut applicability = Applicability::MachineApplicable;
88                 let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
89                 let (span, braced_glob) = if import_source_snippet.is_empty() {
90                     // This is a `_::{_, *}` import
91                     // In this case `use_path.span` is empty and ends directly in front of the `*`,
92                     // so we need to extend it by one byte.
93                     (
94                         use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
95                         true,
96                     )
97                 } else {
98                     // In this case, the `use_path.span` ends right before the `::*`, so we need to
99                     // extend it up to the `*`. Since it is hard to find the `*` in weird
100                     // formattings like `use _ ::  *;`, we extend it up to, but not including the
101                     // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
102                     // can just use the end of the item span
103                     let mut span = use_path.span.with_hi(item.span.hi());
104                     if snippet(cx, span, "").ends_with(';') {
105                         span = use_path.span.with_hi(item.span.hi() - BytePos(1));
106                     }
107                     (
108                         span,
109                         false,
110                     )
111                 };
112
113                 let imports_string = if used_imports.len() == 1 {
114                     used_imports.iter().next().unwrap().to_string()
115                 } else {
116                     let mut imports = used_imports
117                         .iter()
118                         .map(ToString::to_string)
119                         .collect::<Vec<_>>();
120                     imports.sort();
121                     if braced_glob {
122                         imports.join(", ")
123                     } else {
124                         format!("{{{}}}", imports.join(", "))
125                     }
126                 };
127
128                 let sugg = if braced_glob {
129                     imports_string
130                 } else {
131                     format!("{}::{}", import_source_snippet, imports_string)
132                 };
133
134                 let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
135                     (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
136                 } else {
137                     (WILDCARD_IMPORTS, "usage of wildcard import")
138                 };
139
140                 span_lint_and_sugg(
141                     cx,
142                     lint,
143                     span,
144                     message,
145                     "try",
146                     sugg,
147                     applicability,
148                 );
149             }
150         }
151     }
152 }