1 use clippy_utils::diagnostics::span_lint_and_help;
2 use rustc_hir::def_id::DefId;
3 use rustc_hir::{def::Res, HirId, Path, PathSegment};
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_middle::ty::DefIdTree;
6 use rustc_session::{declare_tool_lint, impl_lint_pass};
7 use rustc_span::{sym, symbol::kw, Span};
12 /// Finds items imported through `std` when available through `core`.
14 /// ### Why is this bad?
16 /// Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure
17 /// disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates
18 /// migrating to become `no_std` compatible.
22 /// use std::hash::Hasher;
26 /// use core::hash::Hasher;
28 #[clippy::version = "1.64.0"]
29 pub STD_INSTEAD_OF_CORE,
31 "type is imported from std when available in core"
34 declare_clippy_lint! {
37 /// Finds items imported through `std` when available through `alloc`.
39 /// ### Why is this bad?
41 /// Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from
42 /// alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful
43 /// for crates migrating to become `no_std` compatible.
47 /// use std::vec::Vec;
51 /// # extern crate alloc;
52 /// use alloc::vec::Vec;
54 #[clippy::version = "1.64.0"]
55 pub STD_INSTEAD_OF_ALLOC,
57 "type is imported from std when available in alloc"
60 declare_clippy_lint! {
63 /// Finds items imported through `alloc` when available through `core`.
65 /// ### Why is this bad?
67 /// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
68 /// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
69 /// is also useful for crates migrating to become `no_std` compatible.
73 /// # extern crate alloc;
74 /// use alloc::slice::from_ref;
78 /// use core::slice::from_ref;
80 #[clippy::version = "1.64.0"]
81 pub ALLOC_INSTEAD_OF_CORE,
83 "type is imported from alloc when available in core"
87 pub struct StdReexports {
88 // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
89 // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
90 // when the path could be also be used to access the module.
93 impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
95 impl<'tcx> LateLintPass<'tcx> for StdReexports {
96 fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
97 if let Res::Def(_, def_id) = path.res
98 && let Some(first_segment) = get_first_segment(path)
99 && is_stable(cx, def_id)
101 let (lint, msg, help) = match first_segment.ident.name {
102 sym::std => match cx.tcx.crate_name(def_id.krate) {
105 "used import from `std` instead of `core`",
106 "consider importing the item from `core`",
109 STD_INSTEAD_OF_ALLOC,
110 "used import from `std` instead of `alloc`",
111 "consider importing the item from `alloc`",
114 self.prev_span = path.span;
119 if cx.tcx.crate_name(def_id.krate) == sym::core {
121 ALLOC_INSTEAD_OF_CORE,
122 "used import from `alloc` instead of `core`",
123 "consider importing the item from `core`",
126 self.prev_span = path.span;
132 if path.span != self.prev_span {
133 span_lint_and_help(cx, lint, path.span, msg, None, help);
134 self.prev_span = path.span;
140 /// Returns the first named segment of a [`Path`].
142 /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
144 fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
145 match path.segments {
146 // A global path will have PathRoot as the first segment. In this case, return the segment after.
147 [x, y, ..] if x.ident.name == kw::PathRoot => Some(y),
153 /// Checks if all ancestors of `def_id` are stable, to avoid linting
154 /// [unstable moves](https://github.com/rust-lang/rust/pull/95956)
155 fn is_stable(cx: &LateContext<'_>, mut def_id: DefId) -> bool {
159 .lookup_stability(def_id)
160 .map_or(false, |stability| stability.is_unstable())
165 match cx.tcx.opt_parent(def_id) {
166 Some(parent) => def_id = parent,