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};
10 /// Finds items imported through `std` when available through `core`.
12 /// ### Why is this bad?
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.
20 /// use std::hash::Hasher;
24 /// use core::hash::Hasher;
26 #[clippy::version = "1.64.0"]
27 pub STD_INSTEAD_OF_CORE,
29 "type is imported from std when available in core"
32 declare_clippy_lint! {
35 /// Finds items imported through `std` when available through `alloc`.
37 /// ### Why is this bad?
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.
45 /// use std::vec::Vec;
49 /// # extern crate alloc;
50 /// use alloc::vec::Vec;
52 #[clippy::version = "1.64.0"]
53 pub STD_INSTEAD_OF_ALLOC,
55 "type is imported from std when available in alloc"
58 declare_clippy_lint! {
61 /// Finds items imported through `alloc` when available through `core`.
63 /// ### Why is this bad?
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.
71 /// # extern crate alloc;
72 /// use alloc::slice::from_ref;
76 /// use core::slice::from_ref;
78 #[clippy::version = "1.64.0"]
79 pub ALLOC_INSTEAD_OF_CORE,
81 "type is imported from alloc when available in core"
84 declare_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
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);
97 fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) {
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);
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);
106 // check if the path matches the crate we want to suggest the other path for.
107 if krate == path_root_segment.ident.name;
113 &format!("used import from `{}` instead of `{}`", krate, suggested_crate),
115 &format!("consider importing the item from `{}`", suggested_crate),
121 /// Returns the first named segment of a [`Path`].
123 /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
125 fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
126 let segment = path.segments.first()?;
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 {