Skip to content

Commit e56361f

Browse files
committed
Refactor absolute_paths:
* Check the path length first * Use `is_from_proc_macro` * Use symbols instead of strings when checking crate names
1 parent d2400a4 commit e56361f

File tree

2 files changed

+41
-42
lines changed

2 files changed

+41
-42
lines changed

clippy_lints/src/absolute_paths.rs

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use clippy_utils::diagnostics::span_lint;
2-
use clippy_utils::source::snippet_opt;
2+
use clippy_utils::is_from_proc_macro;
33
use rustc_data_structures::fx::FxHashSet;
44
use rustc_hir::def::{DefKind, Res};
55
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
66
use rustc_hir::{HirId, ItemKind, Node, Path};
77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_session::impl_lint_pass;
99
use rustc_span::symbol::kw;
10+
use rustc_span::Symbol;
1011

1112
declare_clippy_lint! {
1213
/// ### What it does
@@ -23,6 +24,14 @@ declare_clippy_lint! {
2324
/// Note: One exception to this is code from macro expansion - this does not lint such cases, as
2425
/// using absolute paths is the proper way of referencing items in one.
2526
///
27+
/// ### Known issues
28+
///
29+
/// There are currently a few cases which are not caught by this lint:
30+
/// * Macro calls. e.g. `path::to::macro!()`
31+
/// * Derive macros. e.g. `#[derive(path::to::macro)]`
32+
/// * Attribute macros. e.g. `#[path::to::macro]`
33+
/// * Traits in disambiguated paths. e.g. `<T as path::to::trait>::item`
34+
///
2635
/// ### Example
2736
/// ```no_run
2837
/// let x = std::f64::consts::PI;
@@ -47,54 +56,40 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
4756

4857
pub struct AbsolutePaths {
4958
pub absolute_paths_max_segments: u64,
50-
pub absolute_paths_allowed_crates: FxHashSet<String>,
59+
pub absolute_paths_allowed_crates: FxHashSet<Symbol>,
5160
}
5261

53-
impl LateLintPass<'_> for AbsolutePaths {
62+
impl<'tcx> LateLintPass<'tcx> for AbsolutePaths {
5463
// We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath`
5564
// we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't
5665
// 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;
66+
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, hir_id: HirId) {
67+
let (first, len) = match path.segments {
68+
// Any path starting with `::` must have the second segment refer to an external crate.
69+
// Make sure not to include the `::` segment when counting the segments.
70+
[root, first, ..] if root.ident.name == kw::PathRoot => (first, path.segments.len() - 1),
71+
[first, _, ..]
72+
if first.ident.name == kw::Crate
73+
|| matches!(first.res, Res::Def(DefKind::Mod, DefId { index, .. }) if index == CRATE_DEF_INDEX) =>
74+
{
75+
(first, path.segments.len())
76+
},
77+
_ => return,
78+
};
6379

64-
if !path.span.from_expansion()
80+
if len as u64 > self.absolute_paths_max_segments
81+
&& !path.span.from_expansion()
6582
&& 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()
83+
&& !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(..)))
84+
&& !self.absolute_paths_allowed_crates.contains(&first.ident.name)
85+
&& !is_from_proc_macro(cx, path)
7986
{
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-
}
87+
span_lint(
88+
cx,
89+
ABSOLUTE_PATHS,
90+
path.span,
91+
"consider bringing this path into scope with the `use` keyword",
92+
);
9893
}
9994
}
10095
}

clippy_lints/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ use clippy_config::{get_configuration_metadata, Conf};
389389
use clippy_utils::macros::FormatArgsStorage;
390390
use rustc_data_structures::fx::FxHashSet;
391391
use rustc_lint::{Lint, LintId};
392+
use rustc_span::Symbol;
392393
use std::collections::BTreeMap;
393394

394395
/// Register all pre expansion lints
@@ -1128,7 +1129,10 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
11281129
store.register_late_pass(move |_| {
11291130
Box::new(absolute_paths::AbsolutePaths {
11301131
absolute_paths_max_segments,
1131-
absolute_paths_allowed_crates: absolute_paths_allowed_crates.clone(),
1132+
absolute_paths_allowed_crates: absolute_paths_allowed_crates
1133+
.iter()
1134+
.map(|x| Symbol::intern(x))
1135+
.collect(),
11321136
})
11331137
});
11341138
store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals));

0 commit comments

Comments
 (0)