From cb90674aed9ec7b6c7bb471d2ec8cc3b36745b79 Mon Sep 17 00:00:00 2001 From: Jacherr Date: Sat, 11 Nov 2023 00:20:47 +0000 Subject: add iter_over_hash_type lint --- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/iter_over_hash_type.rs | 63 +++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 2 ++ 3 files changed, 66 insertions(+) create mode 100644 clippy_lints/src/iter_over_hash_type.rs (limited to 'clippy_lints') diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 04dcccede08..b4d5e074cdd 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -230,6 +230,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..554790ac61b --- /dev/null +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -0,0 +1,63 @@ +use clippy_utils::paths::{HASHMAP_KEYS, HASHMAP_VALUES, HASHSET_ITER_TY}; +use clippy_utils::{diagnostics::span_lint, match_def_path}; +use clippy_utils::higher::ForLoop; +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 + /// a pseudo-random 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 = new Hashmap(); + /// for (key, value) in my_map { /* ... */ } + /// ``` + /// Use instead: + /// ```no_run + /// let my_map = new Hashmap(); + /// let mut keys = my_map.keys().clone().collect::(); + /// keys.sort(); + /// for key in keys { + /// let value = &my_map[value]; + /// } + /// ``` + #[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_def_path(cx, did, &HASHMAP_KEYS) + || match_def_path(cx, did, &HASHMAP_VALUES) + || match_def_path(cx, did, &HASHSET_ITER_TY) + || 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, + "iterating over unordered hash-based type", + ); + }; + } +} + diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e5f8bd5c471..9bdf6b0224d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -165,6 +165,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; @@ -1065,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, 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` } -- cgit 1.4.1-3-g733a5 From 7bc39f3af855868705b697888383ce00eb6d1bf4 Mon Sep 17 00:00:00 2001 From: Jacherr Date: Sat, 11 Nov 2023 00:21:35 +0000 Subject: format and fix examples --- clippy_lints/src/iter_over_hash_type.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'clippy_lints') diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index 554790ac61b..607d8e5fc28 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -1,6 +1,7 @@ -use clippy_utils::paths::{HASHMAP_KEYS, HASHMAP_VALUES, HASHSET_ITER_TY}; -use clippy_utils::{diagnostics::span_lint, match_def_path}; +use clippy_utils::diagnostics::span_lint; use clippy_utils::higher::ForLoop; +use clippy_utils::match_def_path; +use clippy_utils::paths::{HASHMAP_KEYS, HASHMAP_VALUES, 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}; @@ -18,16 +19,16 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// let my_map = new Hashmap(); + /// let my_map = std::collections::HashMap::::new(); /// for (key, value) in my_map { /* ... */ } /// ``` /// Use instead: /// ```no_run - /// let my_map = new Hashmap(); - /// let mut keys = my_map.keys().clone().collect::(); + /// let my_map = std::collections::HashMap::::new(); + /// let mut keys = my_map.keys().clone().collect::>(); /// keys.sort(); /// for key in keys { - /// let value = &my_map[value]; + /// let value = &my_map[key]; /// } /// ``` #[clippy::version = "1.75.0"] @@ -45,10 +46,10 @@ impl LateLintPass<'_> for IterOverHashType { && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() && let Some(adt) = ty.ty_adt_def() && let did = adt.did() - && (match_def_path(cx, did, &HASHMAP_KEYS) + && (match_def_path(cx, did, &HASHMAP_KEYS) || match_def_path(cx, did, &HASHMAP_VALUES) || match_def_path(cx, did, &HASHSET_ITER_TY) - || is_type_diagnostic_item(cx, ty, sym::HashMap) + || is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::HashSet)) { span_lint( @@ -60,4 +61,3 @@ impl LateLintPass<'_> for IterOverHashType { }; } } - -- cgit 1.4.1-3-g733a5 From 941164807f235e6609e697fa32e983f417d878ed Mon Sep 17 00:00:00 2001 From: Jacherr Date: Sat, 11 Nov 2023 21:26:50 +0000 Subject: implement more types to lint, fix wording --- clippy_lints/src/iter_over_hash_type.rs | 29 ++++++++++++---- clippy_utils/src/paths.rs | 5 +++ tests/ui/iter_over_hash_type.rs | 21 ++++++++++-- tests/ui/iter_over_hash_type.stderr | 60 +++++++++++++++++++++++++++------ 4 files changed, 95 insertions(+), 20 deletions(-) (limited to 'clippy_lints') diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index 607d8e5fc28..d0fedcfc2f4 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -1,8 +1,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::higher::ForLoop; -use clippy_utils::match_def_path; -use clippy_utils::paths::{HASHMAP_KEYS, HASHMAP_VALUES, HASHSET_ITER_TY}; +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 clippy_utils::match_any_def_paths; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -13,7 +16,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Because hash types are unordered, when iterated through such as in a for loop, the values are returned in - /// a pseudo-random order. As a result, on redundant systems this may cause inconsistencies and anomalies. + /// 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. /// @@ -46,9 +49,21 @@ impl LateLintPass<'_> for IterOverHashType { && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() && let Some(adt) = ty.ty_adt_def() && let did = adt.did() - && (match_def_path(cx, did, &HASHMAP_KEYS) - || match_def_path(cx, did, &HASHMAP_VALUES) - || match_def_path(cx, did, &HASHSET_ITER_TY) + && (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)) { @@ -56,7 +71,7 @@ impl LateLintPass<'_> for IterOverHashType { cx, ITER_OVER_HASH_TYPE, expr.span, - "iterating over unordered hash-based type", + "iteration over unordered hash-based type", ); }; } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 9c9fb381e54..0a820a1754c 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -32,10 +32,15 @@ pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncRead pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; +pub const HASHMAP_ITER: [&str; 5] = ["std", "collections", "hash", "map", "Iter"]; +pub const HASHMAP_ITER_MUT: [&str; 5] = ["std", "collections", "hash", "map", "IterMut"]; pub const HASHMAP_KEYS: [&str; 5] = ["std", "collections", "hash", "map", "Keys"]; pub const HASHMAP_VALUES: [&str; 5] = ["std", "collections", "hash", "map", "Values"]; +pub const HASHMAP_DRAIN: [&str; 5] = ["std", "collections", "hash", "map", "Drain"]; +pub const HASHMAP_VALUES_MUT: [&str; 5] = ["std", "collections", "hash", "map", "ValuesMut"]; pub const HASHSET_ITER_TY: [&str; 5] = ["std", "collections", "hash", "set", "Iter"]; pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"]; +pub const HASHSET_DRAIN: [&str; 5] = ["std", "collections", "hash", "set", "Drain"]; pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; diff --git a/tests/ui/iter_over_hash_type.rs b/tests/ui/iter_over_hash_type.rs index fc340abf146..9c3f5d33752 100644 --- a/tests/ui/iter_over_hash_type.rs +++ b/tests/ui/iter_over_hash_type.rs @@ -6,8 +6,8 @@ use std::collections::{HashMap, HashSet}; extern crate proc_macros; fn main() { - let hash_set = HashSet::::new(); - let hash_map = HashMap::::new(); + let mut hash_set = HashSet::::new(); + let mut hash_map = HashMap::::new(); let vec = Vec::::new(); for x in &hash_set { @@ -16,7 +16,10 @@ fn main() { for x in hash_set.iter() { let _ = x; } - for x in hash_set { + for x in hash_set.clone() { + let _ = x; + } + for x in hash_set.drain() { let _ = x; } for (x, y) in &hash_map { @@ -28,6 +31,18 @@ fn main() { for x in hash_map.values() { let _ = x; } + for x in hash_map.values_mut() { + *x += 1; + } + for x in hash_map.iter() { + let _ = x; + } + for x in hash_map.clone() { + let _ = x; + } + for x in hash_map.drain() { + let _ = x; + } // shouldnt fire for x in &vec { diff --git a/tests/ui/iter_over_hash_type.stderr b/tests/ui/iter_over_hash_type.stderr index 72d621c7b59..1204b88d283 100644 --- a/tests/ui/iter_over_hash_type.stderr +++ b/tests/ui/iter_over_hash_type.stderr @@ -1,4 +1,4 @@ -error: iterating over unordered hash-based type +error: iteration over unordered hash-based type --> $DIR/iter_over_hash_type.rs:13:5 | LL | / for x in &hash_set { @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::iter-over-hash-type` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_over_hash_type)]` -error: iterating over unordered hash-based type +error: iteration over unordered hash-based type --> $DIR/iter_over_hash_type.rs:16:5 | LL | / for x in hash_set.iter() { @@ -17,37 +17,77 @@ LL | | let _ = x; LL | | } | |_____^ -error: iterating over unordered hash-based type +error: iteration over unordered hash-based type --> $DIR/iter_over_hash_type.rs:19:5 | -LL | / for x in hash_set { +LL | / for x in hash_set.clone() { LL | | let _ = x; LL | | } | |_____^ -error: iterating over unordered hash-based type +error: iteration over unordered hash-based type --> $DIR/iter_over_hash_type.rs:22:5 | +LL | / for x in hash_set.drain() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:25:5 + | LL | / for (x, y) in &hash_map { LL | | let _ = (x, y); LL | | } | |_____^ -error: iterating over unordered hash-based type - --> $DIR/iter_over_hash_type.rs:25:5 +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:28:5 | LL | / for x in hash_map.keys() { LL | | let _ = x; LL | | } | |_____^ -error: iterating over unordered hash-based type - --> $DIR/iter_over_hash_type.rs:28:5 +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:31:5 | LL | / for x in hash_map.values() { LL | | let _ = x; LL | | } | |_____^ -error: aborting due to 6 previous errors +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:34:5 + | +LL | / for x in hash_map.values_mut() { +LL | | *x += 1; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:37:5 + | +LL | / for x in hash_map.iter() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:40:5 + | +LL | / for x in hash_map.clone() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:43:5 + | +LL | / for x in hash_map.drain() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 938984a24e09b20bf972eff0a6e790b61b482b85 Mon Sep 17 00:00:00 2001 From: Jacherr Date: Sat, 11 Nov 2023 21:29:09 +0000 Subject: run cargo dev fmt --- clippy_lints/src/iter_over_hash_type.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'clippy_lints') diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index d0fedcfc2f4..7755adc4c1d 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -1,11 +1,11 @@ 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 clippy_utils::match_any_def_paths; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -- cgit 1.4.1-3-g733a5