]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/absolute_paths.rs
bump version to 1.80.1+dfsg1-1~bpo12+pve1
[rustc.git] / src / tools / clippy / clippy_lints / src / absolute_paths.rs
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;
10
11 declare_clippy_lint! {
12 /// ### What it does
13 /// Checks for usage of items through absolute paths, like `std::env::current_dir`.
14 ///
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.
19 ///
20 /// The default maximum segments (2) is pretty strict, you may want to increase this in
21 /// `clippy.toml`.
22 ///
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.
25 ///
26 /// ### Example
27 /// ```no_run
28 /// let x = std::f64::consts::PI;
29 /// ```
30 /// Use any of the below instead, or anything else:
31 /// ```no_run
32 /// use std::f64;
33 /// use std::f64::consts;
34 /// use std::f64::consts::PI;
35 /// let x = f64::consts::PI;
36 /// let x = consts::PI;
37 /// let x = PI;
38 /// use std::f64::consts as f64_consts;
39 /// let x = f64_consts::PI;
40 /// ```
41 #[clippy::version = "1.73.0"]
42 pub ABSOLUTE_PATHS,
43 restriction,
44 "checks for usage of an item without a `use` statement"
45 }
46 impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
47
48 pub struct AbsolutePaths {
49 pub absolute_paths_max_segments: u64,
50 pub absolute_paths_allowed_crates: FxHashSet<String>,
51 }
52
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
56 // a `Use`
57 #[expect(clippy::cast_possible_truncation)]
58 fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) {
59 let Self {
60 absolute_paths_max_segments,
61 absolute_paths_allowed_crates,
62 } = self;
63
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
68 // Handle `::std`
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)
73 } else {
74 (first, path.segments.len())
75 }
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()
79 {
80 let is_abs_external =
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;
83
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")
86 {
87 return;
88 }
89
90 if is_abs_external || is_abs_crate {
91 span_lint(
92 cx,
93 ABSOLUTE_PATHS,
94 path.span,
95 "consider bringing this path into scope with the `use` keyword",
96 );
97 }
98 }
99 }
100 }