about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/iter_over_hash_type.rs78
-rw-r--r--clippy_lints/src/lib.rs2
3 files changed, 81 insertions, 0 deletions
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index ecfe8e1582c..85854a0dfb7 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -231,6 +231,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO,
     crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO,
     crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
+    crate::iter_over_hash_type::ITER_OVER_HASH_TYPE_INFO,
     crate::iter_without_into_iter::INTO_ITER_WITHOUT_ITER_INFO,
     crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO,
     crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs
new file mode 100644
index 00000000000..7755adc4c1d
--- /dev/null
+++ b/clippy_lints/src/iter_over_hash_type.rs
@@ -0,0 +1,78 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::higher::ForLoop;
+use clippy_utils::match_any_def_paths;
+use clippy_utils::paths::{
+    HASHMAP_DRAIN, HASHMAP_ITER, HASHMAP_ITER_MUT, HASHMAP_KEYS, HASHMAP_VALUES, HASHMAP_VALUES_MUT, HASHSET_DRAIN,
+    HASHSET_ITER_TY,
+};
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// This is a restriction lint which prevents the use of hash types (i.e., `HashSet` and `HashMap`) in for loops.
+    ///
+    /// ### Why is this bad?
+    /// Because hash types are unordered, when iterated through such as in a for loop, the values are returned in
+    /// an undefined order. As a result, on redundant systems this may cause inconsistencies and anomalies.
+    /// In addition, the unknown order of the elements may reduce readability or introduce other undesired
+    /// side effects.
+    ///
+    /// ### Example
+    /// ```no_run
+    ///     let my_map = std::collections::HashMap::<i32, String>::new();
+    ///     for (key, value) in my_map { /* ... */ }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    ///     let my_map = std::collections::HashMap::<i32, String>::new();
+    ///     let mut keys = my_map.keys().clone().collect::<Vec<_>>();
+    ///     keys.sort();
+    ///     for key in keys {
+    ///         let value = &my_map[key];
+    ///     }
+    /// ```
+    #[clippy::version = "1.75.0"]
+    pub ITER_OVER_HASH_TYPE,
+    restriction,
+    "iterating over unordered hash-based types (`HashMap` and `HashSet`)"
+}
+
+declare_lint_pass!(IterOverHashType => [ITER_OVER_HASH_TYPE]);
+
+impl LateLintPass<'_> for IterOverHashType {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) {
+        if let Some(for_loop) = ForLoop::hir(expr)
+            && !for_loop.body.span.from_expansion()
+            && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs()
+            && let Some(adt) = ty.ty_adt_def()
+            && let did = adt.did()
+            && (match_any_def_paths(
+                cx,
+                did,
+                &[
+                    &HASHMAP_KEYS,
+                    &HASHMAP_VALUES,
+                    &HASHMAP_VALUES_MUT,
+                    &HASHMAP_ITER,
+                    &HASHMAP_ITER_MUT,
+                    &HASHMAP_DRAIN,
+                    &HASHSET_ITER_TY,
+                    &HASHSET_DRAIN,
+                ],
+            )
+            .is_some()
+                || is_type_diagnostic_item(cx, ty, sym::HashMap)
+                || is_type_diagnostic_item(cx, ty, sym::HashSet))
+        {
+            span_lint(
+                cx,
+                ITER_OVER_HASH_TYPE,
+                expr.span,
+                "iteration over unordered hash-based type",
+            );
+        };
+    }
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 463f880aa8a..c462c933082 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -164,6 +164,7 @@ mod item_name_repetitions;
 mod items_after_statements;
 mod items_after_test_module;
 mod iter_not_returning_iterator;
+mod iter_over_hash_type;
 mod iter_without_into_iter;
 mod large_const_arrays;
 mod large_enum_variant;
@@ -1064,6 +1065,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     });
     store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv())));
     store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
+    store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }