]>
Commit | Line | Data |
---|---|---|
136023e0 XL |
1 | use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; |
2 | ||
3 | use rustc_data_structures::fx::FxHashMap; | |
4 | use rustc_errors::Applicability; | |
c295e0f8 | 5 | use rustc_hir::{def::Res, def_id::DefId, Item, ItemKind, UseKind}; |
136023e0 XL |
6 | use rustc_lint::{LateContext, LateLintPass, LintContext}; |
7 | use rustc_session::{declare_tool_lint, impl_lint_pass}; | |
8 | use rustc_span::Symbol; | |
9 | ||
10 | use crate::utils::conf::Rename; | |
11 | ||
12 | declare_clippy_lint! { | |
94222f64 XL |
13 | /// ### What it does |
14 | /// Checks for imports that do not rename the item as specified | |
136023e0 XL |
15 | /// in the `enforce-import-renames` config option. |
16 | /// | |
94222f64 XL |
17 | /// ### Why is this bad? |
18 | /// Consistency is important, if a project has defined import | |
136023e0 XL |
19 | /// renames they should be followed. More practically, some item names are too |
20 | /// vague outside of their defining scope this can enforce a more meaningful naming. | |
21 | /// | |
94222f64 | 22 | /// ### Example |
136023e0 XL |
23 | /// An example clippy.toml configuration: |
24 | /// ```toml | |
25 | /// # clippy.toml | |
26 | /// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }] | |
27 | /// ``` | |
28 | /// | |
29 | /// ```rust,ignore | |
30 | /// use serde_json::Value; | |
31 | /// ``` | |
32 | /// Use instead: | |
33 | /// ```rust,ignore | |
34 | /// use serde_json::Value as JsonValue; | |
35 | /// ``` | |
a2a8927a | 36 | #[clippy::version = "1.55.0"] |
136023e0 XL |
37 | pub MISSING_ENFORCED_IMPORT_RENAMES, |
38 | restriction, | |
39 | "enforce import renames" | |
40 | } | |
41 | ||
42 | pub struct ImportRename { | |
43 | conf_renames: Vec<Rename>, | |
44 | renames: FxHashMap<DefId, Symbol>, | |
45 | } | |
46 | ||
47 | impl ImportRename { | |
48 | pub fn new(conf_renames: Vec<Rename>) -> Self { | |
49 | Self { | |
50 | conf_renames, | |
51 | renames: FxHashMap::default(), | |
52 | } | |
53 | } | |
54 | } | |
55 | ||
56 | impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); | |
57 | ||
58 | impl LateLintPass<'_> for ImportRename { | |
c295e0f8 | 59 | fn check_crate(&mut self, cx: &LateContext<'_>) { |
136023e0 XL |
60 | for Rename { path, rename } in &self.conf_renames { |
61 | if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &path.split("::").collect::<Vec<_>>()) { | |
62 | self.renames.insert(id, Symbol::intern(rename)); | |
63 | } | |
64 | } | |
65 | } | |
66 | ||
67 | fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { | |
68 | if_chain! { | |
69 | if let ItemKind::Use(path, UseKind::Single) = &item.kind; | |
70 | if let Res::Def(_, id) = path.res; | |
71 | if let Some(name) = self.renames.get(&id); | |
72 | // Remove semicolon since it is not present for nested imports | |
73 | let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); | |
74 | if let Some(snip) = snippet_opt(cx, span_without_semi); | |
75 | if let Some(import) = match snip.split_once(" as ") { | |
76 | None => Some(snip.as_str()), | |
77 | Some((import, rename)) => { | |
a2a8927a | 78 | if rename.trim() == name.as_str() { |
136023e0 XL |
79 | None |
80 | } else { | |
81 | Some(import.trim()) | |
82 | } | |
83 | }, | |
84 | }; | |
85 | then { | |
86 | span_lint_and_sugg( | |
87 | cx, | |
88 | MISSING_ENFORCED_IMPORT_RENAMES, | |
89 | span_without_semi, | |
90 | "this import should be renamed", | |
91 | "try", | |
92 | format!( | |
93 | "{} as {}", | |
94 | import, | |
95 | name, | |
96 | ), | |
97 | Applicability::MachineApplicable, | |
98 | ); | |
99 | } | |
100 | } | |
101 | } | |
102 | } |