about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/default_instead_of_iter_empty.rs68
-rw-r--r--clippy_lints/src/lib.register_all.rs1
-rw-r--r--clippy_lints/src/lib.register_lints.rs1
-rw-r--r--clippy_lints/src/lib.register_style.rs1
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_utils/src/paths.rs1
-rw-r--r--tests/ui/default_instead_of_iter_empty.fixed21
-rw-r--r--tests/ui/default_instead_of_iter_empty.rs21
-rw-r--r--tests/ui/default_instead_of_iter_empty.stderr22
10 files changed, 139 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6aaf12ed932..d64dd772ac5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3348,6 +3348,7 @@ Released 2018-09-13
 [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 [`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
+[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
 [`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 [`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 [`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs
new file mode 100644
index 00000000000..3c996d3d2ae
--- /dev/null
+++ b/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -0,0 +1,68 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_def_path, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It checks for `std::iter::Empty::default()` and suggests replacing it with
+    /// `std::iter::empty()`.
+    /// ### Why is this bad?
+    /// `std::iter::empty()` is the more idiomatic way.
+    /// ### Example
+    /// ```rust
+    /// let _ = std::iter::Empty::<usize>::default();
+    /// let iter: std::iter::Empty<usize> = std::iter::Empty::default();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let _ = std::iter::empty::<usize>();
+    /// let iter: std::iter::Empty<usize> = std::iter::empty();
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub DEFAULT_INSTEAD_OF_ITER_EMPTY,
+    style,
+    "check `std::iter::Empty::default()` and replace with `std::iter::empty()`"
+}
+declare_lint_pass!(DefaultIterEmpty => [DEFAULT_INSTEAD_OF_ITER_EMPTY]);
+
+impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Call(iter_expr, []) = &expr.kind
+            && let ExprKind::Path(QPath::TypeRelative(ty, _)) = &iter_expr.kind
+            && let TyKind::Path(ty_path) = &ty.kind
+            && let QPath::Resolved(None, path) = ty_path
+            && let def::Res::Def(_, def_id) = &path.res
+            && match_def_path(cx, *def_id, &paths::ITER_EMPTY)
+        {
+            let mut applicability = Applicability::MachineApplicable;
+            let sugg = make_sugg(cx, ty_path, &mut applicability);
+            span_lint_and_sugg(
+                cx,
+                DEFAULT_INSTEAD_OF_ITER_EMPTY,
+                expr.span,
+                "`std::iter::empty()` is the more idiomatic way",
+                "try",
+                sugg,
+                applicability,
+            );
+        }
+    }
+}
+
+fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String {
+    if let Some(last) = last_path_segment(ty_path).args
+        && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
+            GenericArg::Type(ty) => Some(ty),
+            _ => None,
+        })
+    {
+        format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability))
+    } else {
+        "std::iter::empty()".to_owned()
+    }
+}
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 8a2cfbff953..7876f21f6e6 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -43,6 +43,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(copies::IF_SAME_THEN_ELSE),
     LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
     LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
     LintId::of(derive::DERIVE_HASH_XOR_EQ),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 8ad984c68b8..de22f50cf94 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -105,6 +105,7 @@ store.register_lints(&[
     dbg_macro::DBG_MACRO,
     default::DEFAULT_TRAIT_ACCESS,
     default::FIELD_REASSIGN_WITH_DEFAULT,
+    default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY,
     default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
     default_union_representation::DEFAULT_UNION_REPRESENTATION,
     dereference::EXPLICIT_DEREF_METHODS,
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index b6992ae0ad2..d52ec50e542 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -14,6 +14,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(collapsible_if::COLLAPSIBLE_IF),
     LintId::of(comparison_chain::COMPARISON_CHAIN),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
     LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
     LintId::of(disallowed_methods::DISALLOWED_METHODS),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 84898eae05a..dcee11cc28c 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -199,6 +199,7 @@ mod crate_in_macro_def;
 mod create_dir;
 mod dbg_macro;
 mod default;
+mod default_instead_of_iter_empty;
 mod default_numeric_fallback;
 mod default_union_representation;
 mod dereference;
@@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
     store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
     store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
+    store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 89789c3d851..b31e2c1cdb1 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -62,6 +62,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
+pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
 pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
diff --git a/tests/ui/default_instead_of_iter_empty.fixed b/tests/ui/default_instead_of_iter_empty.fixed
new file mode 100644
index 00000000000..f1abfdcd6ce
--- /dev/null
+++ b/tests/ui/default_instead_of_iter_empty.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+#![warn(clippy::default_instead_of_iter_empty)]
+#![allow(dead_code)]
+use std::collections::HashMap;
+
+#[derive(Default)]
+struct Iter {
+    iter: std::iter::Empty<usize>,
+}
+
+fn main() {
+    // Do lint.
+    let _ = std::iter::empty::<usize>();
+    let _ = std::iter::empty::<HashMap<usize, usize>>();
+    let _foo: std::iter::Empty<usize> = std::iter::empty();
+
+    // Do not lint.
+    let _ = Vec::<usize>::default();
+    let _ = String::default();
+    let _ = Iter::default();
+}
diff --git a/tests/ui/default_instead_of_iter_empty.rs b/tests/ui/default_instead_of_iter_empty.rs
new file mode 100644
index 00000000000..2630519c46d
--- /dev/null
+++ b/tests/ui/default_instead_of_iter_empty.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+#![warn(clippy::default_instead_of_iter_empty)]
+#![allow(dead_code)]
+use std::collections::HashMap;
+
+#[derive(Default)]
+struct Iter {
+    iter: std::iter::Empty<usize>,
+}
+
+fn main() {
+    // Do lint.
+    let _ = std::iter::Empty::<usize>::default();
+    let _ = std::iter::Empty::<HashMap<usize, usize>>::default();
+    let _foo: std::iter::Empty<usize> = std::iter::Empty::default();
+
+    // Do not lint.
+    let _ = Vec::<usize>::default();
+    let _ = String::default();
+    let _ = Iter::default();
+}
diff --git a/tests/ui/default_instead_of_iter_empty.stderr b/tests/ui/default_instead_of_iter_empty.stderr
new file mode 100644
index 00000000000..460fc84def8
--- /dev/null
+++ b/tests/ui/default_instead_of_iter_empty.stderr
@@ -0,0 +1,22 @@
+error: `std::iter::empty()` is the more idiomatic way
+  --> $DIR/default_instead_of_iter_empty.rs:13:13
+   |
+LL |     let _ = std::iter::Empty::<usize>::default();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty::<usize>()`
+   |
+   = note: `-D clippy::default-instead-of-iter-empty` implied by `-D warnings`
+
+error: `std::iter::empty()` is the more idiomatic way
+  --> $DIR/default_instead_of_iter_empty.rs:14:13
+   |
+LL |     let _ = std::iter::Empty::<HashMap<usize, usize>>::default();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty::<HashMap<usize, usize>>()`
+
+error: `std::iter::empty()` is the more idiomatic way
+  --> $DIR/default_instead_of_iter_empty.rs:15:41
+   |
+LL |     let _foo: std::iter::Empty<usize> = std::iter::Empty::default();
+   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: aborting due to 3 previous errors
+