Skip to content

Commit 0156861

Browse files
committed
feat(lint): add default_iter_empty
1 parent b3c94c0 commit 0156861

10 files changed

+115
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3348,6 +3348,7 @@ Released 2018-09-13
33483348
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
33493349
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
33503350
[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
3351+
[`default_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_iter_empty
33513352
[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
33523353
[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
33533354
[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::last_path_segment;
3+
use clippy_utils::source::snippet_with_applicability;
4+
use clippy_utils::{match_def_path, paths};
5+
use rustc_errors::Applicability;
6+
use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
7+
use rustc_lint::{LateContext, LateLintPass};
8+
use rustc_session::{declare_lint_pass, declare_tool_lint};
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// It checks for `std::iter::Empty::default()` and suggests replacing it with
13+
/// `std::iter::empty()`.
14+
/// ### Why is this bad?
15+
/// `std::iter::empty()` is shorter than `std::iter::Empty::default()` and returns an equivalent result
16+
/// ### Example
17+
/// ```rust
18+
/// let _ = std::iter::Empty::<usize>::default();
19+
/// let iter: std::iter::Empty<usize> = std::iter::Empty::default();
20+
/// ```
21+
/// Use instead:
22+
/// ```rust
23+
/// let _ = std::iter::empty::<usize>();
24+
/// let iter: std::iter::Empty<usize> = std::iter::empty();
25+
/// ```
26+
#[clippy::version = "1.63.0"]
27+
pub DEFAULT_ITER_EMPTY,
28+
style,
29+
"Check `std::iter::Empty::default()` and replace with `std::iter::empty()`"
30+
}
31+
declare_lint_pass!(DefaultIterEmpty => [DEFAULT_ITER_EMPTY]);
32+
33+
impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
34+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
35+
if let ExprKind::Call(iter_expr, []) = &expr.kind
36+
&& let ExprKind::Path(QPath::TypeRelative(ty, _)) = &iter_expr.kind
37+
&& let TyKind::Path(ty_path) = &ty.kind
38+
&& let QPath::Resolved(None, path) = ty_path
39+
&& let def::Res::Def(_, def_id) = &path.res
40+
&& match_def_path(cx, *def_id, &paths::ITER_EMPTY) {
41+
let mut applicability = Applicability::MachineApplicable;
42+
let sugg = make_sugg(cx, ty_path, &mut applicability);
43+
span_lint_and_sugg(
44+
cx,
45+
DEFAULT_ITER_EMPTY,
46+
expr.span,
47+
"`std::iter::empty()` is shorter than `std::iter::Empty::default()` and same functionality",
48+
"consider calling `std::iter::empty()` instead",
49+
sugg,
50+
applicability
51+
);
52+
}
53+
}
54+
}
55+
56+
fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String {
57+
if let Some(last) = last_path_segment(ty_path).args
58+
&& let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
59+
GenericArg::Type(ty) => Some(ty),
60+
_ => None,
61+
}) {
62+
format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability))
63+
} else {
64+
format!("std::iter::empty()")
65+
}
66+
}

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
4343
LintId::of(copies::IF_SAME_THEN_ELSE),
4444
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
4545
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
46+
LintId::of(default_iter_empty::DEFAULT_ITER_EMPTY),
4647
LintId::of(dereference::NEEDLESS_BORROW),
4748
LintId::of(derivable_impls::DERIVABLE_IMPLS),
4849
LintId::of(derive::DERIVE_HASH_XOR_EQ),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ store.register_lints(&[
105105
dbg_macro::DBG_MACRO,
106106
default::DEFAULT_TRAIT_ACCESS,
107107
default::FIELD_REASSIGN_WITH_DEFAULT,
108+
default_iter_empty::DEFAULT_ITER_EMPTY,
108109
default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
109110
default_union_representation::DEFAULT_UNION_REPRESENTATION,
110111
dereference::EXPLICIT_DEREF_METHODS,

clippy_lints/src/lib.register_style.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
1414
LintId::of(collapsible_if::COLLAPSIBLE_IF),
1515
LintId::of(comparison_chain::COMPARISON_CHAIN),
1616
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
17+
LintId::of(default_iter_empty::DEFAULT_ITER_EMPTY),
1718
LintId::of(dereference::NEEDLESS_BORROW),
1819
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
1920
LintId::of(disallowed_methods::DISALLOWED_METHODS),

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ mod crate_in_macro_def;
201201
mod create_dir;
202202
mod dbg_macro;
203203
mod default;
204+
mod default_iter_empty;
204205
mod default_numeric_fallback;
205206
mod default_union_representation;
206207
mod dereference;
@@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
910911
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
911912
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
912913
store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
914+
store.register_late_pass(|| Box::new(default_iter_empty::DefaultIterEmpty));
913915
// add lints here, do not remove this comment, it's used in `new_lint`
914916
}
915917

clippy_utils/src/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
6262
pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
6363
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
6464
pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
65+
pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
6566
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
6667
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
6768
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];

tests/ui/default_iter_empty.fixed

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// run-rustfix
2+
#![warn(clippy::default_iter_empty)]
3+
use std::collections::HashMap;
4+
5+
fn main() {
6+
// Do lint.
7+
let _ = std::iter::empty::<usize>();
8+
let _ = std::iter::empty::<HashMap<usize, usize>>();
9+
let _foo: std::iter::Empty<usize> = std::iter::empty();
10+
}

tests/ui/default_iter_empty.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// run-rustfix
2+
#![warn(clippy::default_iter_empty)]
3+
use std::collections::HashMap;
4+
5+
fn main() {
6+
// Do lint.
7+
let _ = std::iter::Empty::<usize>::default();
8+
let _ = std::iter::Empty::<HashMap<usize, usize>>::default();
9+
let _foo: std::iter::Empty<usize> = std::iter::Empty::default();
10+
}

tests/ui/default_iter_empty.stderr

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: `std::iter::empty()` is shorter than `std::iter::Empty::default()` and same functionality
2+
--> $DIR/default_iter_empty.rs:7:13
3+
|
4+
LL | let _ = std::iter::Empty::<usize>::default();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::iter::empty()` instead: `std::iter::empty::<usize>()`
6+
|
7+
= note: `-D clippy::default-iter-empty` implied by `-D warnings`
8+
9+
error: `std::iter::empty()` is shorter than `std::iter::Empty::default()` and same functionality
10+
--> $DIR/default_iter_empty.rs:8:13
11+
|
12+
LL | let _ = std::iter::Empty::<HashMap<usize, usize>>::default();
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::iter::empty()` instead: `std::iter::empty::<HashMap<usize, usize>>()`
14+
15+
error: `std::iter::empty()` is shorter than `std::iter::Empty::default()` and same functionality
16+
--> $DIR/default_iter_empty.rs:9:41
17+
|
18+
LL | let _foo: std::iter::Empty<usize> = std::iter::Empty::default();
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::iter::empty()` instead: `std::iter::empty()`
20+
21+
error: aborting due to 3 previous errors
22+

0 commit comments

Comments
 (0)