]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/std_instead_of_core.rs
Auto merge of #9148 - arieluy:then_some_unwrap_or, r=Jarcho
[rust.git] / 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, Lint};
4 use rustc_session::{declare_lint_pass, declare_tool_lint};
5 use rustc_span::{sym, symbol::kw, Symbol};
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 alloc 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 declare_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
85
86 impl<'tcx> LateLintPass<'tcx> for StdReexports {
87     fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
88         // std_instead_of_core
89         check_path(cx, path, sym::std, sym::core, STD_INSTEAD_OF_CORE);
90         // std_instead_of_alloc
91         check_path(cx, path, sym::std, sym::alloc, STD_INSTEAD_OF_ALLOC);
92         // alloc_instead_of_core
93         check_path(cx, path, sym::alloc, sym::core, ALLOC_INSTEAD_OF_CORE);
94     }
95 }
96
97 fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) {
98     if_chain! {
99         // check if path resolves to the suggested crate.
100         if let Res::Def(_, def_id) = path.res;
101         if suggested_crate == cx.tcx.crate_name(def_id.krate);
102
103         // check if the first segment of the path is the crate we want to identify
104         if let Some(path_root_segment) = get_first_segment(path);
105
106         // check if the path matches the crate we want to suggest the other path for.
107         if krate == path_root_segment.ident.name;
108         then {
109             span_lint_and_help(
110                 cx,
111                 lint,
112                 path.span,
113                 &format!("used import from `{}` instead of `{}`", krate, suggested_crate),
114                 None,
115                 &format!("consider importing the item from `{}`", suggested_crate),
116             );
117         }
118     }
119 }
120
121 /// Returns the first named segment of a [`Path`].
122 ///
123 /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
124 /// is returned.
125 fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
126     let segment = path.segments.first()?;
127
128     // A global path will have PathRoot as the first segment. In this case, return the segment after.
129     if segment.ident.name == kw::PathRoot {
130         path.segments.get(1)
131     } else {
132         Some(segment)
133     }
134 }