1 use clippy_utils
::diagnostics
::span_lint
;
2 use clippy_utils
::source
::snippet_opt
;
3 use rustc_data_structures
::fx
::FxHashSet
;
4 use rustc_hir
::def
::{DefKind, Res}
;
5 use rustc_hir
::def_id
::{DefId, CRATE_DEF_INDEX}
;
6 use rustc_hir
::{HirId, ItemKind, Node, Path}
;
7 use rustc_lint
::{LateContext, LateLintPass}
;
8 use rustc_session
::impl_lint_pass
;
9 use rustc_span
::symbol
::kw
;
11 declare_clippy_lint
! {
13 /// Checks for usage of items through absolute paths, like `std::env::current_dir`.
15 /// ### Why is this bad?
16 /// Many codebases have their own style when it comes to importing, but one that is seldom used
17 /// is using absolute paths *everywhere*. This is generally considered unidiomatic, and you
18 /// should add a `use` statement.
20 /// The default maximum segments (2) is pretty strict, you may want to increase this in
23 /// Note: One exception to this is code from macro expansion - this does not lint such cases, as
24 /// using absolute paths is the proper way of referencing items in one.
28 /// let x = std::f64::consts::PI;
30 /// Use any of the below instead, or anything else:
33 /// use std::f64::consts;
34 /// use std::f64::consts::PI;
35 /// let x = f64::consts::PI;
36 /// let x = consts::PI;
38 /// use std::f64::consts as f64_consts;
39 /// let x = f64_consts::PI;
41 #[clippy::version = "1.73.0"]
44 "checks for usage of an item without a `use` statement"
46 impl_lint_pass
!(AbsolutePaths
=> [ABSOLUTE_PATHS
]);
48 pub struct AbsolutePaths
{
49 pub absolute_paths_max_segments
: u64,
50 pub absolute_paths_allowed_crates
: FxHashSet
<String
>,
53 impl LateLintPass
<'_
> for AbsolutePaths
{
54 // We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath`
55 // we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't
57 #[expect(clippy::cast_possible_truncation)]
58 fn check_path(&mut self, cx
: &LateContext
<'_
>, path
: &Path
<'_
>, hir_id
: HirId
) {
60 absolute_paths_max_segments
,
61 absolute_paths_allowed_crates
,
64 if !path
.span
.from_expansion()
65 && let node
= cx
.tcx
.hir_node(hir_id
)
66 && !matches
!(node
, Node
::Item(item
) if matches
!(item
.kind
, ItemKind
::Use(_
, _
)))
67 && let [first
, rest @
..] = path
.segments
69 && let (segment
, len
) = if first
.ident
.name
== kw
::PathRoot
{
70 // Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1`
71 // is fine here for the same reason
72 (&rest
[0], path
.segments
.len() - 1)
74 (first
, path
.segments
.len())
76 && len
> *absolute_paths_max_segments
as usize
77 && let Some(segment_snippet
) = snippet_opt(cx
, segment
.ident
.span
)
78 && segment_snippet
== segment
.ident
.as_str()
81 matches
!(segment
.res
, Res
::Def(DefKind
::Mod
, DefId { index, .. }
) if index
== CRATE_DEF_INDEX
);
82 let is_abs_crate
= segment
.ident
.name
== kw
::Crate
;
84 if is_abs_external
&& absolute_paths_allowed_crates
.contains(segment
.ident
.name
.as_str())
85 || is_abs_crate
&& absolute_paths_allowed_crates
.contains("crate")
90 if is_abs_external
|| is_abs_crate
{
95 "consider bringing this path into scope with the `use` keyword",