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