0f5338cd90
The `restriction` group contains many lints which are not about necessarily “bad” things, but style choices — perhaps even style choices which contradict conventional Rust style — or are otherwise very situational. This results in silly wording like “Why is this bad? It isn't, but ...”, which I’ve seen confuse a newcomer at least once. To improve this situation, this commit replaces the “Why is this bad?” section heading with “Why restrict this?”, for most, but not all, restriction lints. I left alone the ones whose placement in the restriction group is more incidental. In order to make this make sense, I had to remove the “It isn't, but” texts from the contents of the sections. Sometimes further changes were needed, or there were obvious fixes to make, and I went ahead and made those changes without attempting to split them into another commit, even though many of them are not strictly necessary for the “Why restrict this?” project.
101 lines
3.8 KiB
Rust
101 lines
3.8 KiB
Rust
use clippy_utils::diagnostics::span_lint;
|
|
use clippy_utils::source::snippet_opt;
|
|
use rustc_data_structures::fx::FxHashSet;
|
|
use rustc_hir::def::{DefKind, Res};
|
|
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
|
|
use rustc_hir::{HirId, ItemKind, Node, Path};
|
|
use rustc_lint::{LateContext, LateLintPass};
|
|
use rustc_session::impl_lint_pass;
|
|
use rustc_span::symbol::kw;
|
|
|
|
declare_clippy_lint! {
|
|
/// ### What it does
|
|
/// Checks for usage of items through absolute paths, like `std::env::current_dir`.
|
|
///
|
|
/// ### Why restrict this?
|
|
/// Many codebases have their own style when it comes to importing, but one that is seldom used
|
|
/// is using absolute paths *everywhere*. This is generally considered unidiomatic, and you
|
|
/// should add a `use` statement.
|
|
///
|
|
/// The default maximum segments (2) is pretty strict, you may want to increase this in
|
|
/// `clippy.toml`.
|
|
///
|
|
/// Note: One exception to this is code from macro expansion - this does not lint such cases, as
|
|
/// using absolute paths is the proper way of referencing items in one.
|
|
///
|
|
/// ### Example
|
|
/// ```no_run
|
|
/// let x = std::f64::consts::PI;
|
|
/// ```
|
|
/// Use any of the below instead, or anything else:
|
|
/// ```no_run
|
|
/// use std::f64;
|
|
/// use std::f64::consts;
|
|
/// use std::f64::consts::PI;
|
|
/// let x = f64::consts::PI;
|
|
/// let x = consts::PI;
|
|
/// let x = PI;
|
|
/// use std::f64::consts as f64_consts;
|
|
/// let x = f64_consts::PI;
|
|
/// ```
|
|
#[clippy::version = "1.73.0"]
|
|
pub ABSOLUTE_PATHS,
|
|
restriction,
|
|
"checks for usage of an item without a `use` statement"
|
|
}
|
|
impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
|
|
|
|
pub struct AbsolutePaths {
|
|
pub absolute_paths_max_segments: u64,
|
|
pub absolute_paths_allowed_crates: FxHashSet<String>,
|
|
}
|
|
|
|
impl LateLintPass<'_> for AbsolutePaths {
|
|
// We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath`
|
|
// we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't
|
|
// a `Use`
|
|
#[expect(clippy::cast_possible_truncation)]
|
|
fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) {
|
|
let Self {
|
|
absolute_paths_max_segments,
|
|
absolute_paths_allowed_crates,
|
|
} = self;
|
|
|
|
if !path.span.from_expansion()
|
|
&& let node = cx.tcx.hir_node(hir_id)
|
|
&& !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _)))
|
|
&& let [first, rest @ ..] = path.segments
|
|
// Handle `::std`
|
|
&& let (segment, len) = if first.ident.name == kw::PathRoot {
|
|
// Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1`
|
|
// is fine here for the same reason
|
|
(&rest[0], path.segments.len() - 1)
|
|
} else {
|
|
(first, path.segments.len())
|
|
}
|
|
&& len > *absolute_paths_max_segments as usize
|
|
&& let Some(segment_snippet) = snippet_opt(cx, segment.ident.span)
|
|
&& segment_snippet == segment.ident.as_str()
|
|
{
|
|
let is_abs_external =
|
|
matches!(segment.res, Res::Def(DefKind::Mod, DefId { index, .. }) if index == CRATE_DEF_INDEX);
|
|
let is_abs_crate = segment.ident.name == kw::Crate;
|
|
|
|
if is_abs_external && absolute_paths_allowed_crates.contains(segment.ident.name.as_str())
|
|
|| is_abs_crate && absolute_paths_allowed_crates.contains("crate")
|
|
{
|
|
return;
|
|
}
|
|
|
|
if is_abs_external || is_abs_crate {
|
|
span_lint(
|
|
cx,
|
|
ABSOLUTE_PATHS,
|
|
path.span,
|
|
"consider bringing this path into scope with the `use` keyword",
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|