diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 7bfe9201d812..b77e69b33e7b 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::MaybeResPath as _; use clippy_utils::source::{SpanRangeExt, snippet_opt}; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; +use rustc_hir::{Expr, ExprKind, FnRetTy, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( // skip cast of fn call that returns type alias if let ExprKind::Cast(inner, ..) = expr.kind - && is_cast_from_ty_alias(cx, inner, cast_from) + && is_cast_from_ty_alias(cx, inner) { return false; } @@ -270,34 +270,25 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { /// Finds whether an `Expr` returns a type alias. /// -/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark, -/// dark path reimplementing this (or something similar). -fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool { +/// When in doubt, for example because it calls a non-local function that we don't have the +/// declaration for, assume if might be a type alias. +fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>) -> bool { for_each_expr_without_closures(expr, |expr| { // Calls are a `Path`, and usage of locals are a `Path`. So, this checks // - call() as i32 // - local as i32 if let ExprKind::Path(qpath) = expr.kind { let res = cx.qpath_res(&qpath, expr.hir_id); - // Function call if let Res::Def(DefKind::Fn, def_id) = res { - let Some(snippet) = cx.tcx.def_span(def_id).get_source_text(cx) else { - return ControlFlow::Continue(()); + let Some(def_id) = def_id.as_local() else { + // External function, we can't know, better be safe + return ControlFlow::Break(()); }; - // This is the worst part of this entire function. This is the only way I know of to - // check whether a function returns a type alias. Sure, you can get the return type - // from a function in the current crate as an hir ty, but how do you get it for - // external functions?? Simple: It's impossible. So, we check whether a part of the - // function's declaration snippet is exactly equal to the `Ty`. That way, we can - // see whether it's a type alias. - // - // FIXME: This won't work if the type is given an alias through `use`, should we - // consider this a type alias as well? - if !snippet - .split("->") - .skip(1) - .any(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) + if let Some(FnRetTy::Return(ty)) = cx.tcx.hir_get_fn_output(def_id) + && let TyKind::Path(qpath) = ty.kind + && is_ty_alias(&qpath) { + // Function call to a local function returning a type alias return ControlFlow::Break(()); } // Local usage @@ -305,7 +296,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx && let Node::LetStmt(l) = cx.tcx.parent_hir_node(hir_id) { if let Some(e) = l.init - && is_cast_from_ty_alias(cx, e, cast_from) + && is_cast_from_ty_alias(cx, e) { return ControlFlow::Break::<()>(()); } @@ -323,7 +314,3 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx }) .is_some() } - -fn snippet_eq_ty(snippet: &str, ty: Ty<'_>) -> bool { - snippet.trim() == ty.to_string() || snippet.trim().contains(&format!("::{ty}")) -} diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index a323c7cf8307..69fca3aefcaf 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -245,8 +245,8 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { // We want to lint unsafe blocks #0 and #1 let bad_unsafe_blocks = self .metavar_expns - .iter() - .filter_map(|(_, state)| match state { + .values() + .filter_map(|state| match state { MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()), MetavarState::ReferencedInSafe => None, }) diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 366bfaed73d4..79034fa23300 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -21,6 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'tcx>, // hashmap m_arg: &'tcx Expr<'tcx>, // |(_, v)| v msrv: Msrv, + method_name: Symbol, ) { if map_type == sym::into_iter && !msrv.meets(cx, msrvs::INTO_KEYS) { return; @@ -67,7 +68,7 @@ pub(super) fn check<'tcx>( format!("iterating on a map's {replacement_kind}s"), "try", format!( - "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {body_snippet})", + "{recv_snippet}.{into_prefix}{replacement_kind}s().{method_name}(|{}{bound_ident}| {body_snippet})", annotation.prefix_str(), ), applicability, diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index e3bcca64e923..241791e57c8e 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,13 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_copy}; +use clippy_utils::visitors::for_each_expr_without_closures; +use core::ops::ControlFlow; use rustc_ast::BindingMode; use rustc_errors::Applicability; -use rustc_hir::{Body, Expr, ExprKind, HirId, HirIdSet, PatKind}; +use rustc_hir::{Body, CaptureBy, Closure, Expr, ExprKind, HirId, HirIdSet, Param, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::{FakeReadCause, Mutability}; -use rustc_middle::ty::{self, BorrowKind}; +use rustc_middle::ty::{self, BorrowKind, UpvarCapture}; use rustc_span::{Symbol, sym}; use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED}; @@ -64,6 +66,11 @@ pub(super) fn check<'tcx>( let body @ Body { params: [p], .. } = cx.tcx.hir_body(closure.body) else { return; }; + + if param_captured_by_move_block(cx, body.value, p) { + return; + } + let mut delegate = MoveDelegate { used_move: HirIdSet::default(), }; @@ -140,6 +147,34 @@ struct MoveDelegate { used_move: HirIdSet, } +/// Checks if the expression contains a closure or coroutine with `move` capture semantics that +/// captures the given parameter. +fn param_captured_by_move_block(cx: &LateContext<'_>, expr: &Expr<'_>, param: &Param<'_>) -> bool { + let mut param_hir_ids = HirIdSet::default(); + param.pat.walk(|pat| { + param_hir_ids.insert(pat.hir_id); + true + }); + + for_each_expr_without_closures(expr, |e| { + if let ExprKind::Closure(Closure { + capture_clause: CaptureBy::Value { .. }, + def_id, + .. + }) = e.kind + && cx.tcx.closure_captures(*def_id).iter().any(|capture| { + matches!(capture.info.capture_kind, UpvarCapture::ByValue) + && matches!(capture.place.base, PlaceBase::Upvar(upvar) if param_hir_ids.contains(&upvar.var_path.hir_id)) + }) + { + return ControlFlow::Break(()); + } + + ControlFlow::Continue(()) + }) + .is_some() +} + impl<'tcx> Delegate<'tcx> for MoveDelegate { fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, _: HirId) { if let PlaceBase::Local(l) = place_with_id.place.base { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 264405e6c3fb..ff534abe5353 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5275,6 +5275,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { call_span, self.msrv, ); + if let Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) = method_call(recv) { + iter_kv_map::check(cx, map_name, expr, recv2, arg, self.msrv, sym::filter_map); + } }, (sym::find_map, [arg]) => { unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FindMap); @@ -5285,6 +5288,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { lines_filter_map_ok::check_filter_or_flat_map( cx, expr, recv, "flat_map", arg, call_span, self.msrv, ); + if let Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) = method_call(recv) { + iter_kv_map::check(cx, map_name, expr, recv2, arg, self.msrv, sym::flat_map); + } }, (sym::flatten, []) => { match method_call(recv) { @@ -5383,7 +5389,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { manual_is_variant_and::check_map(cx, expr); match method_call(recv) { Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => { - iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv); + iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv, sym::map); }, Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 3b271ca0dc14..9b327955608a 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -99,10 +99,6 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { self.found = true; return; }, - ExprKind::If(..) => { - self.found = true; - return; - }, ExprKind::Path(_) => { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) && adj diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 509ad4e4fcb3..d14a56115cbd 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -417,6 +417,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { && args.iter().any(|a| a.hir_id == expr.hir_id) && let Res::Def(DefKind::AssocFn, def_id) = expr.res(cx) && cx.tcx.is_diagnostic_item(sym::to_string_method, def_id) + && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) + && args.type_at(0).is_str() { // Detected `ToString::to_string` passed as an argument (generic: any call or method call) span_lint_and_sugg( @@ -425,7 +427,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { expr.span, "`ToString::to_string` used as `&str` to `String` converter", "try", - "ToOwned::to_owned".to_string(), + "str::to_owned".to_string(), Applicability::MachineApplicable, ); } diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 239f721ae05b..67ae1dcef81b 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -820,8 +820,8 @@ pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { matches!( (l, r), (Defaultness::Implicit, Defaultness::Implicit) - | (Defaultness::Default(_), Defaultness::Default(_)) - | (Defaultness::Final(_), Defaultness::Final(_)) + | (Defaultness::Default(_), Defaultness::Default(_)) + | (Defaultness::Final(_), Defaultness::Final(_)) ) } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index c479ea59b884..3dbee9273e25 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2340,12 +2340,11 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(& && let item = tcx.hir_item(id) && let ItemKind::Const(ident, _generics, ty, _body) = item.kind && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind - // We could also check for the type name `test::TestDescAndFn` - && let Res::Def(DefKind::Struct, _) = path.res + // We could also check for the type name `test::TestDescAndFn` + && let Res::Def(DefKind::Struct, _) = path.res + && find_attr!(tcx.hir_attrs(item.hir_id()), AttributeKind::RustcTestMarker(..)) { - if find_attr!(tcx.hir_attrs(item.hir_id()), AttributeKind::RustcTestMarker(..)) { - names.push(ident.name); - } + names.push(ident.name); } } names.sort_unstable(); diff --git a/src/driver.rs b/src/driver.rs index 409eb182fe33..f27cb6708709 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -126,6 +126,7 @@ fn config(&mut self, config: &mut interface::Config) { config.psess_created = Some(Box::new(move |psess| { track_clippy_args(psess, clippy_args_var.as_deref()); })); + config.extra_symbols = sym::EXTRA_SYMBOLS.into(); } } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index fa2b6cf26806..6bdfb17481a3 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -197,10 +197,6 @@ fn base_config(&self, test_dir: &str, mandatory_annotations: bool) -> Config { defaults.set_custom("diagnostic-collector", collector); } config.with_args(&self.args); - let current_exe_path = env::current_exe().unwrap(); - let deps_path = current_exe_path.parent().unwrap(); - let profile_path = deps_path.parent().unwrap(); - config.program.args.extend( [ "--emit=metadata", @@ -224,6 +220,7 @@ fn base_config(&self, test_dir: &str, mandatory_annotations: bool) -> Config { config.program.args.push(format!("--sysroot={sysroot}").into()); } + let profile_path = target_dir.join(env!("PROFILE")); config.program.program = profile_path.join(if cfg!(windows) { "clippy-driver.exe" } else { @@ -469,7 +466,7 @@ enum DiagnosticOrMessage { } /// Collects applicabilities from the diagnostics produced for each UI test, producing the -/// `util/gh-pages/lints.json` file used by +/// `util/gh-pages/index.html` file used by #[derive(Debug, Clone)] struct DiagnosticCollector { sender: Sender>, diff --git a/tests/ui/cmp_owned/with_suggestion.fixed b/tests/ui/cmp_owned/with_suggestion.fixed index f65339605e75..471905b47df7 100644 --- a/tests/ui/cmp_owned/with_suggestion.fixed +++ b/tests/ui/cmp_owned/with_suggestion.fixed @@ -114,6 +114,12 @@ fn issue16322(item: String) { } fn issue16458() { + macro_rules! m { + () => { + "" + }; + } + macro_rules! partly_comes_from_macro { ($i:ident: $ty:ty, $def:expr) => { let _ = { @@ -125,7 +131,7 @@ fn issue16458() { } partly_comes_from_macro! { - required_version: String, env!("HOME").to_string() + required_version: String, m!().to_string() } macro_rules! all_comes_from_macro { @@ -141,6 +147,6 @@ fn issue16458() { }; } all_comes_from_macro! { - required_version: String, env!("HOME").to_string(); + required_version: String, m!().to_string(); } } diff --git a/tests/ui/cmp_owned/with_suggestion.rs b/tests/ui/cmp_owned/with_suggestion.rs index ed2300c80eaa..3323176d34e3 100644 --- a/tests/ui/cmp_owned/with_suggestion.rs +++ b/tests/ui/cmp_owned/with_suggestion.rs @@ -114,6 +114,12 @@ fn issue16322(item: String) { } fn issue16458() { + macro_rules! m { + () => { + "" + }; + } + macro_rules! partly_comes_from_macro { ($i:ident: $ty:ty, $def:expr) => { let _ = { @@ -125,7 +131,7 @@ macro_rules! partly_comes_from_macro { } partly_comes_from_macro! { - required_version: String, env!("HOME").to_string() + required_version: String, m!().to_string() } macro_rules! all_comes_from_macro { @@ -141,6 +147,6 @@ macro_rules! all_comes_from_macro { }; } all_comes_from_macro! { - required_version: String, env!("HOME").to_string(); + required_version: String, m!().to_string(); } } diff --git a/tests/ui/cmp_owned/with_suggestion.stderr b/tests/ui/cmp_owned/with_suggestion.stderr index 38d124baa4b5..3797810e3b98 100644 --- a/tests/ui/cmp_owned/with_suggestion.stderr +++ b/tests/ui/cmp_owned/with_suggestion.stderr @@ -56,13 +56,13 @@ LL | if item == t!(frohes_neu_Jahr).to_string() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(frohes_neu_Jahr)` error: this creates an owned instance just for comparison - --> tests/ui/cmp_owned/with_suggestion.rs:135:51 + --> tests/ui/cmp_owned/with_suggestion.rs:141:51 | LL | let res = <$ty>::default() == "$def".to_string(); | ^^^^^^^^^^^^^^^^^^ help: try: `"$def"` ... LL | / all_comes_from_macro! { -LL | | required_version: String, env!("HOME").to_string(); +LL | | required_version: String, m!().to_string(); LL | | } | |_____- in this macro invocation | diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed index 189d76bc9431..e3ab5fd1e9ef 100644 --- a/tests/ui/iter_kv_map.fixed +++ b/tests/ui/iter_kv_map.fixed @@ -1,5 +1,11 @@ #![warn(clippy::iter_kv_map)] -#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] +#![allow( + unused_mut, + clippy::redundant_clone, + clippy::redundant_closure, + clippy::suspicious_map, + clippy::map_identity +)] use std::collections::{BTreeMap, HashMap}; @@ -195,3 +201,33 @@ fn issue16340() { let _ = hm.keys().map(|key| vec![key]); //~^ iter_kv_map } + +fn issue16515() { + let hash_map: HashMap = HashMap::new(); + hash_map.keys().flat_map(|k| Some(*k)); + //~^ iter_kv_map + + hash_map.values().flat_map(|v| Some(*v)); + //~^ iter_kv_map + + hash_map.keys().filter_map(|k| (k > &0).then_some(1)); + //~^ iter_kv_map + + hash_map.values().filter_map(|v| (v > &0).then_some(1)); + //~^ iter_kv_map + + hash_map.into_keys().flat_map(|k| Some(k)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_values().flat_map(|v| Some(v)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_keys().filter_map(|k| (k > 0).then_some(1)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_values().filter_map(|v| (v > 0).then_some(1)); + //~^ iter_kv_map +} diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs index cfc303447004..903813b1bf62 100644 --- a/tests/ui/iter_kv_map.rs +++ b/tests/ui/iter_kv_map.rs @@ -1,5 +1,11 @@ #![warn(clippy::iter_kv_map)] -#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] +#![allow( + unused_mut, + clippy::redundant_clone, + clippy::redundant_closure, + clippy::suspicious_map, + clippy::map_identity +)] use std::collections::{BTreeMap, HashMap}; @@ -199,3 +205,33 @@ fn issue16340() { let _ = hm.iter().map(|(key, _)| vec![key]); //~^ iter_kv_map } + +fn issue16515() { + let hash_map: HashMap = HashMap::new(); + hash_map.iter().flat_map(|(k, _)| Some(*k)); + //~^ iter_kv_map + + hash_map.iter().flat_map(|(_, v)| Some(*v)); + //~^ iter_kv_map + + hash_map.iter().filter_map(|(k, _)| (k > &0).then_some(1)); + //~^ iter_kv_map + + hash_map.iter().filter_map(|(_, v)| (v > &0).then_some(1)); + //~^ iter_kv_map + + hash_map.into_iter().flat_map(|(k, _)| Some(k)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_iter().flat_map(|(_, v)| Some(v)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_iter().filter_map(|(k, _)| (k > 0).then_some(1)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_iter().filter_map(|(_, v)| (v > 0).then_some(1)); + //~^ iter_kv_map +} diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr index 866e69ea1922..cdfd05fdd09e 100644 --- a/tests/ui/iter_kv_map.stderr +++ b/tests/ui/iter_kv_map.stderr @@ -1,5 +1,5 @@ error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:14:13 + --> tests/ui/iter_kv_map.rs:20:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` @@ -8,174 +8,73 @@ LL | let _ = map.iter().map(|(key, _)| key).collect::>(); = help: to override `-D warnings` add `#[allow(clippy::iter_kv_map)]` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:16:13 + --> tests/ui/iter_kv_map.rs:22:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:18:13 + --> tests/ui/iter_kv_map.rs:24:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:21:13 + --> tests/ui/iter_kv_map.rs:27:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:23:13 + --> tests/ui/iter_kv_map.rs:29:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:26:13 + --> tests/ui/iter_kv_map.rs:32:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:28:13 + --> tests/ui/iter_kv_map.rs:34:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:31:13 + --> tests/ui/iter_kv_map.rs:37:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:33:13 + --> tests/ui/iter_kv_map.rs:39:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:48:13 - | -LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` - -error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:50:13 - | -LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` - -error: iterating on a map's values --> tests/ui/iter_kv_map.rs:54:13 | -LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` - -error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:58:13 - | -LL | let _ = map - | _____________^ -LL | | -LL | | .clone() -LL | | .into_iter() -... | -LL | | val -LL | | }) - | |__________^ - | -help: try - | -LL ~ let _ = map -LL + -LL + .clone().into_values().map(|mut val| { -LL + val += 2; -LL + val -LL + }) - | - -error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:69:13 - | -LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` - -error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:74:13 - | -LL | let _ = map.iter().map(|(key, _)| key).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` - -error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:76:13 - | -LL | let _ = map.iter().map(|(_, value)| value).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` - -error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:78:13 - | -LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` - -error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:81:13 - | -LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` - -error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:83:13 - | -LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` - -error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:86:13 - | -LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` - -error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:88:13 - | -LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` - -error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:91:13 - | -LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` - -error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:93:13 - | -LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` - -error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:108:13 - | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:110:13 + --> tests/ui/iter_kv_map.rs:56:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:114:13 + --> tests/ui/iter_kv_map.rs:60:13 | LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:118:13 + --> tests/ui/iter_kv_map.rs:64:13 | LL | let _ = map | _____________^ @@ -198,82 +97,231 @@ LL + }) | error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:129:13 + --> tests/ui/iter_kv_map.rs:75:13 | LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:145:13 + --> tests/ui/iter_kv_map.rs:80:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:148:13 + --> tests/ui/iter_kv_map.rs:82:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:151:13 + --> tests/ui/iter_kv_map.rs:84:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:160:13 + --> tests/ui/iter_kv_map.rs:87:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:163:13 + --> tests/ui/iter_kv_map.rs:89:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:166:13 + --> tests/ui/iter_kv_map.rs:92:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:169:13 + --> tests/ui/iter_kv_map.rs:94:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:97:13 + | +LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` + error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:172:13 + --> tests/ui/iter_kv_map.rs:99:13 + | +LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:114:13 + | +LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:116:13 + | +LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:120:13 + | +LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:124:13 + | +LL | let _ = map + | _____________^ +LL | | +LL | | .clone() +LL | | .into_iter() +... | +LL | | val +LL | | }) + | |__________^ + | +help: try + | +LL ~ let _ = map +LL + +LL + .clone().into_values().map(|mut val| { +LL + val += 2; +LL + val +LL + }) + | + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:135:13 + | +LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:151:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:154:13 + | +LL | let _ = map.iter().map(|(_, value)| value).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:157:13 + | +LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:166:13 + | +LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:169:13 + | +LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:172:13 + | +LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + error: iterating on a map's values --> tests/ui/iter_kv_map.rs:175:13 | +LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:178:13 + | +LL | let _ = map.iter().map(|(key, _)| key).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:181:13 + | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:178:13 + --> tests/ui/iter_kv_map.rs:184:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:193:13 + --> tests/ui/iter_kv_map.rs:199:13 | LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:199:13 + --> tests/ui/iter_kv_map.rs:205:13 | LL | let _ = hm.iter().map(|(key, _)| vec![key]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hm.keys().map(|key| vec![key])` -error: aborting due to 40 previous errors +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:211:5 + | +LL | hash_map.iter().flat_map(|(k, _)| Some(*k)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.keys().flat_map(|k| Some(*k))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:214:5 + | +LL | hash_map.iter().flat_map(|(_, v)| Some(*v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.values().flat_map(|v| Some(*v))` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:217:5 + | +LL | hash_map.iter().filter_map(|(k, _)| (k > &0).then_some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.keys().filter_map(|k| (k > &0).then_some(1))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:220:5 + | +LL | hash_map.iter().filter_map(|(_, v)| (v > &0).then_some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.values().filter_map(|v| (v > &0).then_some(1))` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:223:5 + | +LL | hash_map.into_iter().flat_map(|(k, _)| Some(k)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_keys().flat_map(|k| Some(k))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:227:5 + | +LL | hash_map.into_iter().flat_map(|(_, v)| Some(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_values().flat_map(|v| Some(v))` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:231:5 + | +LL | hash_map.into_iter().filter_map(|(k, _)| (k > 0).then_some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_keys().filter_map(|k| (k > 0).then_some(1))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:235:5 + | +LL | hash_map.into_iter().filter_map(|(_, v)| (v > 0).then_some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_values().filter_map(|v| (v > 0).then_some(1))` + +error: aborting due to 48 previous errors diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 4171f19469a4..520b4f8be8cf 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -102,3 +102,63 @@ fn main() { fn cloned_flatten(x: Option<&Option>) -> Option { x.cloned().flatten() } + +mod issue_16428 { + #[derive(Clone)] + struct Foo; + + impl Foo { + async fn do_async(&self) {} + } + + fn async_move_map() -> Vec> { + let map: std::collections::HashMap<(), Foo> = std::collections::HashMap::new(); + + // Should NOT lint: async move block captures `item` by value + map.values() + .cloned() + .map(|item| async move { item.do_async().await }) + .collect::>() + } + + fn async_move_for_each() { + let map: std::collections::HashMap<(), Foo> = std::collections::HashMap::new(); + + // Should NOT lint: async move block captures `item` by value + map.values() + .cloned() + .for_each(|item| drop(async move { item.do_async().await })); + } + + fn move_closure() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should NOT lint: move closure captures `x` by value + let _: Vec<_> = vec.iter().cloned().map(|x| move || x.len()).collect(); + } + + fn async_move_not_capturing_param() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should lint: async move captures `y`, not `x` + let _ = vec.iter().map(|x| { + //~^ redundant_iter_cloned + let y = x.len(); + async move { y } + }); + } + + fn move_closure_not_capturing_param() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should lint: move closure captures `y`, not `x` + let _: Vec<_> = vec + //~^ redundant_iter_cloned + .iter() + .map(|x| { + let y = x.len(); + move || y + }) + .collect(); + } +} diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index fe6aba24dd3e..3e79675dd7c6 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -103,3 +103,64 @@ fn bar<'a>(iter: impl Iterator> + 'a, target: String) -> impl I fn cloned_flatten(x: Option<&Option>) -> Option { x.cloned().flatten() } + +mod issue_16428 { + #[derive(Clone)] + struct Foo; + + impl Foo { + async fn do_async(&self) {} + } + + fn async_move_map() -> Vec> { + let map: std::collections::HashMap<(), Foo> = std::collections::HashMap::new(); + + // Should NOT lint: async move block captures `item` by value + map.values() + .cloned() + .map(|item| async move { item.do_async().await }) + .collect::>() + } + + fn async_move_for_each() { + let map: std::collections::HashMap<(), Foo> = std::collections::HashMap::new(); + + // Should NOT lint: async move block captures `item` by value + map.values() + .cloned() + .for_each(|item| drop(async move { item.do_async().await })); + } + + fn move_closure() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should NOT lint: move closure captures `x` by value + let _: Vec<_> = vec.iter().cloned().map(|x| move || x.len()).collect(); + } + + fn async_move_not_capturing_param() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should lint: async move captures `y`, not `x` + let _ = vec.iter().cloned().map(|x| { + //~^ redundant_iter_cloned + let y = x.len(); + async move { y } + }); + } + + fn move_closure_not_capturing_param() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should lint: move closure captures `y`, not `x` + let _: Vec<_> = vec + //~^ redundant_iter_cloned + .iter() + .cloned() + .map(|x| { + let y = x.len(); + move || y + }) + .collect(); + } +} diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index f234d19e4aaa..72b00ca2e32c 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -165,5 +165,47 @@ LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); | | | help: try: `.any(|x| x.len() == 1)` -error: aborting due to 19 previous errors +error: unneeded cloning of iterator items + --> tests/ui/iter_overeager_cloned.rs:145:17 + | +LL | let _ = vec.iter().cloned().map(|x| { + | _________________^ +LL | | +LL | | let y = x.len(); +LL | | async move { y } +LL | | }); + | |__________^ + | +help: try + | +LL ~ let _ = vec.iter().map(|x| { +LL + +LL + let y = x.len(); +LL + async move { y } +LL ~ }); + | + +error: unneeded cloning of iterator items + --> tests/ui/iter_overeager_cloned.rs:156:25 + | +LL | let _: Vec<_> = vec + | _________________________^ +LL | | +LL | | .iter() +LL | | .cloned() +... | +LL | | move || y +LL | | }) + | |______________^ + | +help: try + | +LL ~ .iter() +LL + .map(|x| { +LL + let y = x.len(); +LL + move || y +LL + }) + | + +error: aborting due to 21 previous errors diff --git a/tests/ui/str_to_string.fixed b/tests/ui/str_to_string.fixed index 5b76cf78f069..a8950ab70e56 100644 --- a/tests/ui/str_to_string.fixed +++ b/tests/ui/str_to_string.fixed @@ -1,10 +1,10 @@ #![warn(clippy::str_to_string)] fn main() { - let hello = "hello world".to_owned(); + let hello: String = "hello world".to_owned(); //~^ str_to_string - let msg = &hello[..]; + let msg: &str = &hello[..]; msg.to_owned(); //~^ str_to_string } @@ -19,7 +19,7 @@ fn issue16271(key: &[u8]) { }; } - let _value = t!(str::from_utf8(key)).to_owned(); + let _value: String = t!(str::from_utf8(key)).to_owned(); //~^ str_to_string } @@ -32,22 +32,27 @@ impl GenericWrapper { } fn issue16511(x: Option<&str>) { - let _ = x.map(ToOwned::to_owned); + let _: Option = x.map(str::to_owned); //~^ str_to_string - let _ = x.map(ToOwned::to_owned); + let _: Option = x.map(str::to_owned); //~^ str_to_string - let _ = ["a", "b"].iter().map(ToOwned::to_owned); - //~^ str_to_string + // This should not trigger the lint because ToOwned::to_owned would produce &str, not String. + let _: Vec = ["a", "b"].iter().map(ToString::to_string).collect(); fn mapper String>(f: F) -> String { f("hello") } - let _ = mapper(ToOwned::to_owned); + let _: String = mapper(str::to_owned); //~^ str_to_string - let w = GenericWrapper("hello"); - let _ = w.mapper(ToOwned::to_owned); + let w: GenericWrapper<&str> = GenericWrapper("hello"); + let _: String = w.mapper(str::to_owned); //~^ str_to_string } + +// No lint: non-str types should not trigger str_to_string. See #16569 +fn no_lint_non_str() { + let _: Vec = [1, 2].iter().map(i32::to_string).collect(); +} diff --git a/tests/ui/str_to_string.rs b/tests/ui/str_to_string.rs index f099eb29b1b5..5d78893f53f6 100644 --- a/tests/ui/str_to_string.rs +++ b/tests/ui/str_to_string.rs @@ -1,10 +1,10 @@ #![warn(clippy::str_to_string)] fn main() { - let hello = "hello world".to_string(); + let hello: String = "hello world".to_string(); //~^ str_to_string - let msg = &hello[..]; + let msg: &str = &hello[..]; msg.to_string(); //~^ str_to_string } @@ -19,7 +19,7 @@ macro_rules! t { }; } - let _value = t!(str::from_utf8(key)).to_string(); + let _value: String = t!(str::from_utf8(key)).to_string(); //~^ str_to_string } @@ -32,22 +32,27 @@ fn mapper U>(self, f: F) -> U { } fn issue16511(x: Option<&str>) { - let _ = x.map(ToString::to_string); + let _: Option = x.map(ToString::to_string); //~^ str_to_string - let _ = x.map(str::to_string); + let _: Option = x.map(str::to_string); //~^ str_to_string - let _ = ["a", "b"].iter().map(ToString::to_string); - //~^ str_to_string + // This should not trigger the lint because ToOwned::to_owned would produce &str, not String. + let _: Vec = ["a", "b"].iter().map(ToString::to_string).collect(); fn mapper String>(f: F) -> String { f("hello") } - let _ = mapper(ToString::to_string); + let _: String = mapper(ToString::to_string); //~^ str_to_string - let w = GenericWrapper("hello"); - let _ = w.mapper(ToString::to_string); + let w: GenericWrapper<&str> = GenericWrapper("hello"); + let _: String = w.mapper(ToString::to_string); //~^ str_to_string } + +// No lint: non-str types should not trigger str_to_string. See #16569 +fn no_lint_non_str() { + let _: Vec = [1, 2].iter().map(i32::to_string).collect(); +} diff --git a/tests/ui/str_to_string.stderr b/tests/ui/str_to_string.stderr index 296b8e36f28c..cd4db83e6370 100644 --- a/tests/ui/str_to_string.stderr +++ b/tests/ui/str_to_string.stderr @@ -1,8 +1,8 @@ error: `to_string()` called on a `&str` - --> tests/ui/str_to_string.rs:4:17 + --> tests/ui/str_to_string.rs:4:25 | -LL | let hello = "hello world".to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"hello world".to_owned()` +LL | let hello: String = "hello world".to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"hello world".to_owned()` | = note: `-D clippy::str-to-string` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::str_to_string)]` @@ -14,40 +14,34 @@ LL | msg.to_string(); | ^^^^^^^^^^^^^^^ help: try: `msg.to_owned()` error: `to_string()` called on a `&str` - --> tests/ui/str_to_string.rs:22:18 + --> tests/ui/str_to_string.rs:22:26 | -LL | let _value = t!(str::from_utf8(key)).to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(str::from_utf8(key)).to_owned()` +LL | let _value: String = t!(str::from_utf8(key)).to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(str::from_utf8(key)).to_owned()` error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:35:19 + --> tests/ui/str_to_string.rs:35:35 | -LL | let _ = x.map(ToString::to_string); - | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` +LL | let _: Option = x.map(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `str::to_owned` error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:38:19 + --> tests/ui/str_to_string.rs:38:35 | -LL | let _ = x.map(str::to_string); - | ^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` +LL | let _: Option = x.map(str::to_string); + | ^^^^^^^^^^^^^^ help: try: `str::to_owned` error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:41:35 + --> tests/ui/str_to_string.rs:47:28 | -LL | let _ = ["a", "b"].iter().map(ToString::to_string); - | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` +LL | let _: String = mapper(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `str::to_owned` error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:47:20 + --> tests/ui/str_to_string.rs:51:30 | -LL | let _ = mapper(ToString::to_string); - | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` +LL | let _: String = w.mapper(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `str::to_owned` -error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:51:22 - | -LL | let _ = w.mapper(ToString::to_string); - | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` - -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 91ff4b9ee771..c6e9bc3cba07 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -133,12 +133,13 @@ fn main() { aaa(); //~^ unnecessary_cast let x = aaa(); - aaa(); + x; + //~^ unnecessary_cast + bbb(); //~^ unnecessary_cast - // Will not lint currently. - bbb() as u32; let x = bbb(); - bbb() as u32; + x; + //~^ unnecessary_cast let i8_ptr: *const i8 = &1; let u8_ptr: *const u8 = &1; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 5444a914db16..6936a23d4286 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -133,12 +133,13 @@ pub fn $a() -> $b { aaa() as u32; //~^ unnecessary_cast let x = aaa(); - aaa() as u32; + x as u32; //~^ unnecessary_cast - // Will not lint currently. bbb() as u32; + //~^ unnecessary_cast let x = bbb(); - bbb() as u32; + x as u32; + //~^ unnecessary_cast let i8_ptr: *const i8 = &1; let u8_ptr: *const u8 = &1; diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 3e3c5eb81c10..e4f4309ea716 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -100,170 +100,182 @@ LL | aaa() as u32; error: casting to the same type is unnecessary (`u32` -> `u32`) --> tests/ui/unnecessary_cast.rs:136:5 | -LL | aaa() as u32; - | ^^^^^^^^^^^^ help: try: `aaa()` +LL | x as u32; + | ^^^^^^^^ help: try: `x` + +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> tests/ui/unnecessary_cast.rs:138:5 + | +LL | bbb() as u32; + | ^^^^^^^^^^^^ help: try: `bbb()` + +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> tests/ui/unnecessary_cast.rs:141:5 + | +LL | x as u32; + | ^^^^^^^^ help: try: `x` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:173:9 + --> tests/ui/unnecessary_cast.rs:174:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:175:9 + --> tests/ui/unnecessary_cast.rs:176:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:177:9 + --> tests/ui/unnecessary_cast.rs:178:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:179:17 + --> tests/ui/unnecessary_cast.rs:180:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:181:17 + --> tests/ui/unnecessary_cast.rs:182:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:183:17 + --> tests/ui/unnecessary_cast.rs:184:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:185:9 + --> tests/ui/unnecessary_cast.rs:186:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:187:9 + --> tests/ui/unnecessary_cast.rs:188:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:200:9 + --> tests/ui/unnecessary_cast.rs:201:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:202:9 + --> tests/ui/unnecessary_cast.rs:203:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> tests/ui/unnecessary_cast.rs:204:9 + --> tests/ui/unnecessary_cast.rs:205:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> tests/ui/unnecessary_cast.rs:206:9 + --> tests/ui/unnecessary_cast.rs:207:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:208:9 + --> tests/ui/unnecessary_cast.rs:209:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:211:9 + --> tests/ui/unnecessary_cast.rs:212:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:213:9 + --> tests/ui/unnecessary_cast.rs:214:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:218:17 + --> tests/ui/unnecessary_cast.rs:219:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:220:17 + --> tests/ui/unnecessary_cast.rs:221:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting to the same type is unnecessary (`i32` -> `i32`) - --> tests/ui/unnecessary_cast.rs:227:18 + --> tests/ui/unnecessary_cast.rs:228:18 | LL | let _ = &(x as i32); | ^^^^^^^^^^ help: try: `{ x }` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:234:22 + --> tests/ui/unnecessary_cast.rs:235:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> tests/ui/unnecessary_cast.rs:237:22 + --> tests/ui/unnecessary_cast.rs:238:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:245:22 + --> tests/ui/unnecessary_cast.rs:246:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:248:23 + --> tests/ui/unnecessary_cast.rs:249:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> tests/ui/unnecessary_cast.rs:258:20 + --> tests/ui/unnecessary_cast.rs:259:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:269:9 + --> tests/ui/unnecessary_cast.rs:270:9 | LL | (*x as usize).pow(2) | ^^^^^^^^^^^^^ help: try: `(*x)` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:277:31 + --> tests/ui/unnecessary_cast.rs:278:31 | LL | assert_eq!(vec.len(), x as usize); | ^^^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:280:17 + --> tests/ui/unnecessary_cast.rs:281:17 | LL | let _ = (5i32 as i64 as i64).abs(); | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:283:17 + --> tests/ui/unnecessary_cast.rs:284:17 | LL | let _ = 5i32 as i64 as i64; | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` -error: aborting due to 44 previous errors +error: aborting due to 46 previous errors