]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
Rollup merge of #101266 - LuisCardosoOliveira:translation-rustcsession-pt3, r=davidtwco
[rust.git] / src / tools / clippy / clippy_lints / src / std_instead_of_core.rs
1 use clippy_utils::diagnostics::span_lint_and_help;
2 use rustc_hir::{def::Res, HirId, Path, PathSegment};
3 use rustc_lint::{LateContext, LateLintPass};
4 use rustc_session::{declare_tool_lint, impl_lint_pass};
5 use rustc_span::{sym, symbol::kw, Span};
6
7 declare_clippy_lint! {
8     /// ### What it does
9     ///
10     /// Finds items imported through `std` when available through `core`.
11     ///
12     /// ### Why is this bad?
13     ///
14     /// Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure
15     /// disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates
16     /// migrating to become `no_std` compatible.
17     ///
18     /// ### Example
19     /// ```rust
20     /// use std::hash::Hasher;
21     /// ```
22     /// Use instead:
23     /// ```rust
24     /// use core::hash::Hasher;
25     /// ```
26     #[clippy::version = "1.64.0"]
27     pub STD_INSTEAD_OF_CORE,
28     restriction,
29     "type is imported from std when available in core"
30 }
31
32 declare_clippy_lint! {
33     /// ### What it does
34     ///
35     /// Finds items imported through `std` when available through `alloc`.
36     ///
37     /// ### Why is this bad?
38     ///
39     /// Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from
40     /// alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful
41     /// for crates migrating to become `no_std` compatible.
42     ///
43     /// ### Example
44     /// ```rust
45     /// use std::vec::Vec;
46     /// ```
47     /// Use instead:
48     /// ```rust
49     /// # extern crate alloc;
50     /// use alloc::vec::Vec;
51     /// ```
52     #[clippy::version = "1.64.0"]
53     pub STD_INSTEAD_OF_ALLOC,
54     restriction,
55     "type is imported from std when available in alloc"
56 }
57
58 declare_clippy_lint! {
59     /// ### What it does
60     ///
61     /// Finds items imported through `alloc` when available through `core`.
62     ///
63     /// ### Why is this bad?
64     ///
65     /// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
66     /// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
67     /// is also useful for crates migrating to become `no_std` compatible.
68     ///
69     /// ### Example
70     /// ```rust
71     /// # extern crate alloc;
72     /// use alloc::slice::from_ref;
73     /// ```
74     /// Use instead:
75     /// ```rust
76     /// use core::slice::from_ref;
77     /// ```
78     #[clippy::version = "1.64.0"]
79     pub ALLOC_INSTEAD_OF_CORE,
80     restriction,
81     "type is imported from alloc when available in core"
82 }
83
84 #[derive(Default)]
85 pub struct StdReexports {
86     // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
87     // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
88     // when the path could be also be used to access the module.
89     prev_span: Span,
90 }
91 impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
92
93 impl<'tcx> LateLintPass<'tcx> for StdReexports {
94     fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
95         if let Res::Def(_, def_id) = path.res
96             && let Some(first_segment) = get_first_segment(path)
97         {
98             let (lint, msg, help) = match first_segment.ident.name {
99                 sym::std => match cx.tcx.crate_name(def_id.krate) {
100                     sym::core => (
101                         STD_INSTEAD_OF_CORE,
102                         "used import from `std` instead of `core`",
103                         "consider importing the item from `core`",
104                     ),
105                     sym::alloc => (
106                         STD_INSTEAD_OF_ALLOC,
107                         "used import from `std` instead of `alloc`",
108                         "consider importing the item from `alloc`",
109                     ),
110                     _ => {
111                         self.prev_span = path.span;
112                         return;
113                     },
114                 },
115                 sym::alloc => {
116                     if cx.tcx.crate_name(def_id.krate) == sym::core {
117                         (
118                             ALLOC_INSTEAD_OF_CORE,
119                             "used import from `alloc` instead of `core`",
120                             "consider importing the item from `core`",
121                         )
122                     } else {
123                         self.prev_span = path.span;
124                         return;
125                     }
126                 },
127                 _ => return,
128             };
129             if path.span != self.prev_span {
130                 span_lint_and_help(cx, lint, path.span, msg, None, help);
131                 self.prev_span = path.span;
132             }
133         }
134     }
135 }
136
137 /// Returns the first named segment of a [`Path`].
138 ///
139 /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
140 /// is returned.
141 fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
142     match path.segments {
143         // A global path will have PathRoot as the first segment. In this case, return the segment after.
144         [x, y, ..] if x.ident.name == kw::PathRoot => Some(y),
145         [x, ..] => Some(x),
146         _ => None,
147     }
148 }