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};
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 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.
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"
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.
91 impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
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)
98 let (lint, msg, help) = match first_segment.ident.name {
99 sym::std => match cx.tcx.crate_name(def_id.krate) {
102 "used import from `std` instead of `core`",
103 "consider importing the item from `core`",
106 STD_INSTEAD_OF_ALLOC,
107 "used import from `std` instead of `alloc`",
108 "consider importing the item from `alloc`",
111 self.prev_span = path.span;
116 if cx.tcx.crate_name(def_id.krate) == sym::core {
118 ALLOC_INSTEAD_OF_CORE,
119 "used import from `alloc` instead of `core`",
120 "consider importing the item from `core`",
123 self.prev_span = path.span;
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;
137 /// Returns the first named segment of a [`Path`].
139 /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
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),