Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clippy_config/src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ define_Conf! {
/// Which crates to allow absolute paths from
#[lints(absolute_paths)]
absolute_paths_allowed_crates: Vec<String> = Vec::new(),
/// The maximum number of times an absolute path can appear in a module before being linted.
absolute_paths_max_occurrences: u64 = 0,
/// The maximum number of segments a path can have before being linted, anything above this will
/// be linted.
#[lints(absolute_paths)]
Expand Down
75 changes: 60 additions & 15 deletions clippy_lints/src/absolute_paths.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_from_proc_macro;
use clippy_utils::{fulfill_or_allowed, is_from_proc_macro};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_INDEX, DefId};
use rustc_hir::{HirId, ItemKind, Node, Path};
use rustc_hir::{HirId, ItemKind, Node, OwnerNode, Path};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::Symbol;
use rustc_span::symbol::kw;
use rustc_span::{Span, Symbol};

declare_clippy_lint! {
/// ### What it does
Expand Down Expand Up @@ -56,19 +56,23 @@ declare_clippy_lint! {
impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);

pub struct AbsolutePaths {
pub absolute_paths_max_segments: u64,
pub absolute_paths_allowed_crates: FxHashSet<Symbol>,
pub max_segments: u64,
pub allowed_crates: FxHashSet<Symbol>,
pub max_occurrences: u64,
occurrences: Vec<((DefId, DefId), Span)>, // To track count of occurences
}

impl AbsolutePaths {
pub fn new(conf: &'static Conf) -> Self {
Self {
absolute_paths_max_segments: conf.absolute_paths_max_segments,
absolute_paths_allowed_crates: conf
max_segments: conf.absolute_paths_max_segments,
allowed_crates: conf
.absolute_paths_allowed_crates
.iter()
.map(|x| Symbol::intern(x))
.collect(),
max_occurrences: conf.absolute_paths_max_occurrences,
occurrences: Vec::new(),
}
}
}
Expand All @@ -93,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for AbsolutePaths {
&& let has_root = s1.ident.name == kw::PathRoot
&& let first = if has_root { s2 } else { s1 }
&& let len = segments.len() - usize::from(has_root)
&& len as u64 > self.absolute_paths_max_segments
&& len as u64 > self.max_segments
&& let crate_name = if let Res::Def(DefKind::Mod, DefId { index, .. }) = first.res
&& index == CRATE_DEF_INDEX
{
Expand All @@ -108,15 +112,56 @@ impl<'tcx> LateLintPass<'tcx> for AbsolutePaths {
&& !path.span.from_expansion()
&& let node = cx.tcx.hir_node(hir_id)
&& !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(..)))
&& !self.absolute_paths_allowed_crates.contains(&crate_name)
&& !self.allowed_crates.contains(&crate_name)
&& !is_from_proc_macro(cx, path)
{
span_lint(
cx,
ABSOLUTE_PATHS,
path.span,
"consider bringing this path into scope with the `use` keyword",
);
if self.max_occurrences == 0 {
// Default behaviour : emit lint directly, no need for occurence tracking
span_lint(
cx,
ABSOLUTE_PATHS,
path.span,
"consider bringing this path into scope with the `use` keyword",
);
} else if let Res::Def(_, def_id) = path.res
&& !fulfill_or_allowed(cx, ABSOLUTE_PATHS, [hir_id])
{
// Occurence based behaviour : accumulate spans and emit lint in check_crate_post
// if the occurence count is exceeded
let module = cx
.tcx
.hir_parent_owner_iter(hir_id)
.find(|(_, node)| {
if let OwnerNode::Item(item) = node {
matches!(item.kind, ItemKind::Mod(..))
} else {
matches!(node, OwnerNode::Crate(..))
}
})
.map(|(id, _)| id);
if let Some(module) = module {
self.occurrences.push(((def_id, module.to_def_id()), path.span));
}
}
}
}

fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
// Only runs when absolute_paths_max_occurrences > 0.
// Emit lints for any path that exceeded the per-file occurrence threshold.
self.occurrences
.sort_by_key(|((item_did, mod_did), span)| (item_did.index.as_u32(), mod_did.index.as_u32(), span.lo()));
for chunk in self.occurrences.chunk_by(|(key1, _), (key2, _)| key1 == key2) {
if chunk.len() as u64 > self.max_occurrences {
for (_, span) in chunk {
span_lint(
cx,
ABSOLUTE_PATHS,
*span,
"this absolute path is used too many times, consider bringing it into scope with the `use` keyword",
);
}
}
Comment on lines +152 to +164
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still doesn't emit in a stable order. To do that you'll have to sort by span after filtering out all the small groups. Easiest way is to collect all the lint spans into a separate vec.

}
}
}
112 changes: 104 additions & 8 deletions tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr
Original file line number Diff line number Diff line change
@@ -1,44 +1,140 @@
error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13
--> tests/ui-toml/absolute_paths/absolute_paths.rs:15:13
|
LL | let _ = std::path::is_separator(' ');
| ^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
--> tests/ui-toml/absolute_paths/absolute_paths.rs:8:9
|
LL | #![deny(clippy::absolute_paths)]
| ^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13
--> tests/ui-toml/absolute_paths/absolute_paths.rs:21:13
|
LL | let _ = ::std::path::MAIN_SEPARATOR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
--> tests/ui-toml/absolute_paths/absolute_paths.rs:26:13
|
LL | let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31
--> tests/ui-toml/absolute_paths/absolute_paths.rs:33:31
|
LL | let _: &std::path::Path = std::path::Path::new("");
| ^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13
--> tests/ui-toml/absolute_paths/absolute_paths.rs:33:13
|
LL | let _: &std::path::Path = std::path::Path::new("");
| ^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13
--> tests/ui-toml/absolute_paths/absolute_paths.rs:48:13
|
LL | let _ = std::option::Option::None::<i32>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 6 previous errors
error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:111:17
|
LL | let _ = std::io::ErrorKind::Other;
| ^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:115:17
|
LL | let _ = std::io::ErrorKind::Other;
| ^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:121:17
|
LL | let _ = std::env::current_dir();
| ^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:126:17
|
LL | let _ = std::env::current_dir();
| ^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:131:17
|
LL | let _ = std::env::current_dir();
| ^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:138:17
|
LL | let _ = std::fs::read_dir(".");
| ^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:142:17
|
LL | let _ = std::fs::read_dir(".");
| ^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:146:17
|
LL | let _ = std::fs::metadata(".");
| ^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:150:17
|
LL | let _ = std::fs::metadata(".");
| ^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:157:17
|
LL | let _ = std::collections::btree_map::BTreeMap::from([("Mercury", 0.4)]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:162:17
|
LL | let _ = std::collections::btree_map::BTreeMap::from([("Mercury", 0.4)]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:171:17
|
LL | let _ = std::collections::linked_list::LinkedList::from([1]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:176:17
|
LL | let _ = std::collections::linked_list::LinkedList::from([1]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:208:17
|
LL | let _ = std::env::current_dir();
| ^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:213:17
|
LL | let _ = std::env::current_dir();
| ^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:218:17
|
LL | let _ = std::env::current_dir();
| ^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 22 previous errors

30 changes: 27 additions & 3 deletions tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
--> tests/ui-toml/absolute_paths/absolute_paths.rs:26:13
|
LL | let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
--> tests/ui-toml/absolute_paths/absolute_paths.rs:8:9
|
LL | #![deny(clippy::absolute_paths)]
| ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error
error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:157:17
|
LL | let _ = std::collections::btree_map::BTreeMap::from([("Mercury", 0.4)]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:162:17
|
LL | let _ = std::collections::btree_map::BTreeMap::from([("Mercury", 0.4)]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:171:17
|
LL | let _ = std::collections::linked_list::LinkedList::from([1]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: consider bringing this path into scope with the `use` keyword
--> tests/ui-toml/absolute_paths/absolute_paths.rs:176:17
|
LL | let _ = std::collections::linked_list::LinkedList::from([1]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 5 previous errors

Loading
Loading