Auto merge of #12432 - Ethiraric:fix-12411, r=y21
[`unused_enumerate_index`]: trigger on method calls The lint used to check for patterns looking like: ```rs for (_, x) in some_iter.enumerate() { // Index is ignored } ``` This commit further checks for chained method calls constructs where we can detect that the index is unused. Currently, this checks only for the following patterns: ```rs some_iter.enumerate().map_function(|(_, x)| ..) let x = some_iter.enumerate(); x.map_function(|(_, x)| ..) ``` where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or `map`. Fixes #12411. *Please write a short comment explaining your change (or "none" for internal only changes)* changelog: [`unused_enumerate_index`]: add detection for method chains such as `iter.enumerate().map(|(_, x)| x)`
This commit is contained in:
commit
5a11fefc25
@ -1,62 +1,41 @@
|
|||||||
use super::UNUSED_ENUMERATE_INDEX;
|
use super::UNUSED_ENUMERATE_INDEX;
|
||||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::{pat_is_wild, sugg};
|
use clippy_utils::{match_def_path, pat_is_wild, sugg};
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
|
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
|
|
||||||
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
|
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
|
||||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
|
///
|
||||||
let PatKind::Tuple([index, elem], _) = pat.kind else {
|
/// The lint is also partially implemented in `clippy_lints/src/methods/unused_enumerate_index.rs`.
|
||||||
return;
|
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) {
|
||||||
};
|
if let PatKind::Tuple([index, elem], _) = pat.kind
|
||||||
|
&& let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind
|
||||||
let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind else {
|
&& let ty = cx.typeck_results().expr_ty(arg)
|
||||||
return;
|
&& pat_is_wild(cx, &index.kind, body)
|
||||||
};
|
&& let ty::Adt(base, _) = *ty.kind()
|
||||||
|
&& match_def_path(cx, base.did(), &clippy_utils::paths::CORE_ITER_ENUMERATE_STRUCT)
|
||||||
let ty = cx.typeck_results().expr_ty(arg);
|
&& let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
|
||||||
|
&& match_def_path(cx, call_id, &clippy_utils::paths::CORE_ITER_ENUMERATE_METHOD)
|
||||||
if !pat_is_wild(cx, &index.kind, body) {
|
{
|
||||||
return;
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
UNUSED_ENUMERATE_INDEX,
|
||||||
|
arg.span,
|
||||||
|
"you seem to use `.enumerate()` and immediately discard the index",
|
||||||
|
|diag| {
|
||||||
|
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
|
||||||
|
multispan_sugg(
|
||||||
|
diag,
|
||||||
|
"remove the `.enumerate()` call",
|
||||||
|
vec![
|
||||||
|
(pat.span, snippet(cx, elem.span, "..").into_owned()),
|
||||||
|
(arg.span, base_iter.to_string()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = match *ty.kind() {
|
|
||||||
ty::Adt(base, _substs) => cx.tcx.def_path_str(base.did()),
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
if name != "std::iter::Enumerate" && name != "core::iter::Enumerate" {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let call_name = cx.tcx.def_path_str(call_id);
|
|
||||||
|
|
||||||
if call_name != "std::iter::Iterator::enumerate" && call_name != "core::iter::Iterator::enumerate" {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
span_lint_and_then(
|
|
||||||
cx,
|
|
||||||
UNUSED_ENUMERATE_INDEX,
|
|
||||||
arg.span,
|
|
||||||
"you seem to use `.enumerate()` and immediately discard the index",
|
|
||||||
|diag| {
|
|
||||||
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
|
|
||||||
multispan_sugg(
|
|
||||||
diag,
|
|
||||||
"remove the `.enumerate()` call",
|
|
||||||
vec![
|
|
||||||
(pat.span, snippet(cx, elem.span, "..").into_owned()),
|
|
||||||
(arg.span, base_iter.to_string()),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,7 @@
|
|||||||
mod unnecessary_result_map_or_else;
|
mod unnecessary_result_map_or_else;
|
||||||
mod unnecessary_sort_by;
|
mod unnecessary_sort_by;
|
||||||
mod unnecessary_to_owned;
|
mod unnecessary_to_owned;
|
||||||
|
mod unused_enumerate_index;
|
||||||
mod unwrap_expect_used;
|
mod unwrap_expect_used;
|
||||||
mod useless_asref;
|
mod useless_asref;
|
||||||
mod utils;
|
mod utils;
|
||||||
@ -4429,6 +4430,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||||||
zst_offset::check(cx, expr, recv);
|
zst_offset::check(cx, expr, recv);
|
||||||
},
|
},
|
||||||
("all", [arg]) => {
|
("all", [arg]) => {
|
||||||
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
||||||
iter_overeager_cloned::check(
|
iter_overeager_cloned::check(
|
||||||
cx,
|
cx,
|
||||||
@ -4447,23 +4449,26 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
|
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
("any", [arg]) => match method_call(recv) {
|
("any", [arg]) => {
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
cx,
|
match method_call(recv) {
|
||||||
expr,
|
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||||
recv,
|
cx,
|
||||||
recv2,
|
expr,
|
||||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
recv,
|
||||||
false,
|
recv2,
|
||||||
),
|
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||||
Some(("chars", recv, _, _, _))
|
false,
|
||||||
if let ExprKind::Closure(arg) = arg.kind
|
),
|
||||||
&& let body = cx.tcx.hir().body(arg.body)
|
Some(("chars", recv, _, _, _))
|
||||||
&& let [param] = body.params =>
|
if let ExprKind::Closure(arg) = arg.kind
|
||||||
{
|
&& let body = cx.tcx.hir().body(arg.body)
|
||||||
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
|
&& let [param] = body.params =>
|
||||||
},
|
{
|
||||||
_ => {},
|
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
("arg", [arg]) => {
|
("arg", [arg]) => {
|
||||||
suspicious_command_arg_space::check(cx, recv, arg, span);
|
suspicious_command_arg_space::check(cx, recv, arg, span);
|
||||||
@ -4596,14 +4601,17 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
("filter_map", [arg]) => {
|
("filter_map", [arg]) => {
|
||||||
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
unnecessary_filter_map::check(cx, expr, arg, name);
|
unnecessary_filter_map::check(cx, expr, arg, name);
|
||||||
filter_map_bool_then::check(cx, expr, arg, call_span);
|
filter_map_bool_then::check(cx, expr, arg, call_span);
|
||||||
filter_map_identity::check(cx, expr, arg, span);
|
filter_map_identity::check(cx, expr, arg, span);
|
||||||
},
|
},
|
||||||
("find_map", [arg]) => {
|
("find_map", [arg]) => {
|
||||||
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
unnecessary_filter_map::check(cx, expr, arg, name);
|
unnecessary_filter_map::check(cx, expr, arg, name);
|
||||||
},
|
},
|
||||||
("flat_map", [arg]) => {
|
("flat_map", [arg]) => {
|
||||||
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
flat_map_identity::check(cx, expr, arg, span);
|
flat_map_identity::check(cx, expr, arg, span);
|
||||||
flat_map_option::check(cx, expr, arg, span);
|
flat_map_option::check(cx, expr, arg, span);
|
||||||
},
|
},
|
||||||
@ -4625,17 +4633,20 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||||||
manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv);
|
manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv);
|
||||||
unnecessary_fold::check(cx, expr, init, acc, span);
|
unnecessary_fold::check(cx, expr, init, acc, span);
|
||||||
},
|
},
|
||||||
("for_each", [arg]) => match method_call(recv) {
|
("for_each", [arg]) => {
|
||||||
Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
match method_call(recv) {
|
||||||
cx,
|
Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
|
||||||
expr,
|
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||||
recv,
|
cx,
|
||||||
recv2,
|
expr,
|
||||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
recv,
|
||||||
false,
|
recv2,
|
||||||
),
|
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||||
_ => {},
|
false,
|
||||||
|
),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
("get", [arg]) => {
|
("get", [arg]) => {
|
||||||
get_first::check(cx, expr, recv, arg);
|
get_first::check(cx, expr, recv, arg);
|
||||||
@ -4682,6 +4693,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||||||
},
|
},
|
||||||
(name @ ("map" | "map_err"), [m_arg]) => {
|
(name @ ("map" | "map_err"), [m_arg]) => {
|
||||||
if name == "map" {
|
if name == "map" {
|
||||||
|
unused_enumerate_index::check(cx, expr, recv, m_arg);
|
||||||
map_clone::check(cx, expr, recv, m_arg, &self.msrv);
|
map_clone::check(cx, expr, recv, m_arg, &self.msrv);
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => {
|
Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => {
|
||||||
|
135
clippy_lints/src/methods/unused_enumerate_index.rs
Normal file
135
clippy_lints/src/methods/unused_enumerate_index.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then};
|
||||||
|
use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT};
|
||||||
|
use clippy_utils::source::{snippet, snippet_opt};
|
||||||
|
use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild};
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::ty::AdtDef;
|
||||||
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
|
use crate::loops::UNUSED_ENUMERATE_INDEX;
|
||||||
|
|
||||||
|
/// Check for the `UNUSED_ENUMERATE_INDEX` lint outside of loops.
|
||||||
|
///
|
||||||
|
/// The lint is declared in `clippy_lints/src/loops/mod.rs`. There, the following pattern is
|
||||||
|
/// checked:
|
||||||
|
/// ```ignore
|
||||||
|
/// for (_, x) in some_iter.enumerate() {
|
||||||
|
/// // Index is ignored
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This `check` function checks for chained method calls constructs where we can detect that the
|
||||||
|
/// index is unused. Currently, this checks only for the following patterns:
|
||||||
|
/// ```ignore
|
||||||
|
/// some_iter.enumerate().map_function(|(_, x)| ..)
|
||||||
|
/// let x = some_iter.enumerate();
|
||||||
|
/// x.map_function(|(_, x)| ..)
|
||||||
|
/// ```
|
||||||
|
/// where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or
|
||||||
|
/// `map`.
|
||||||
|
///
|
||||||
|
/// # Preconditions
|
||||||
|
/// This function must be called not on the `enumerate` call expression itself, but on any of the
|
||||||
|
/// map functions listed above. It will ensure that `recv` is a `std::iter::Enumerate` instance and
|
||||||
|
/// that the method call is one of the `std::iter::Iterator` trait.
|
||||||
|
///
|
||||||
|
/// * `call_expr`: The map function call expression
|
||||||
|
/// * `recv`: The receiver of the call
|
||||||
|
/// * `closure_arg`: The argument to the map function call containing the closure/function to apply
|
||||||
|
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
|
||||||
|
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||||
|
if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did)
|
||||||
|
// If we call a method on a `std::iter::Enumerate` instance
|
||||||
|
&& match_def_path(cx, recv_ty_defid, &CORE_ITER_ENUMERATE_STRUCT)
|
||||||
|
// If we are calling a method of the `Iterator` trait
|
||||||
|
&& is_trait_method(cx, call_expr, sym::Iterator)
|
||||||
|
// And the map argument is a closure
|
||||||
|
&& let ExprKind::Closure(closure) = closure_arg.kind
|
||||||
|
&& let closure_body = cx.tcx.hir().body(closure.body)
|
||||||
|
// And that closure has one argument ...
|
||||||
|
&& let [closure_param] = closure_body.params
|
||||||
|
// .. which is a tuple of 2 elements
|
||||||
|
&& let PatKind::Tuple([index, elem], ..) = closure_param.pat.kind
|
||||||
|
// And that the first element (the index) is either `_` or unused in the body
|
||||||
|
&& pat_is_wild(cx, &index.kind, closure_body)
|
||||||
|
// Try to find the initializer for `recv`. This is needed in case `recv` is a local_binding. In the
|
||||||
|
// first example below, `expr_or_init` would return `recv`.
|
||||||
|
// ```
|
||||||
|
// iter.enumerate().map(|(_, x)| x)
|
||||||
|
// ^^^^^^^^^^^^^^^^ `recv`, a call to `std::iter::Iterator::enumerate`
|
||||||
|
//
|
||||||
|
// let binding = iter.enumerate();
|
||||||
|
// ^^^^^^^^^^^^^^^^ `recv_init_expr`
|
||||||
|
// binding.map(|(_, x)| x)
|
||||||
|
// ^^^^^^^ `recv`, not a call to `std::iter::Iterator::enumerate`
|
||||||
|
// ```
|
||||||
|
&& let recv_init_expr = expr_or_init(cx, recv)
|
||||||
|
// Make sure the initializer is a method call. It may be that the `Enumerate` comes from something
|
||||||
|
// that we cannot control.
|
||||||
|
// This would for instance happen with:
|
||||||
|
// ```
|
||||||
|
// external_lib::some_function_returning_enumerate().map(|(_, x)| x)
|
||||||
|
// ```
|
||||||
|
&& let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind
|
||||||
|
&& let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id)
|
||||||
|
// Make sure the method call is `std::iter::Iterator::enumerate`.
|
||||||
|
&& match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD)
|
||||||
|
{
|
||||||
|
// Check if the tuple type was explicit. It may be the type system _needs_ the type of the element
|
||||||
|
// that would be explicited in the closure.
|
||||||
|
let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
|
||||||
|
// We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
|
||||||
|
// Fallback to `..` if we fail getting either snippet.
|
||||||
|
Some(ty_span) => snippet_opt(cx, elem.span)
|
||||||
|
.and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}")))
|
||||||
|
.unwrap_or_else(|| "..".to_string()),
|
||||||
|
// Otherwise, we have no explicit type. We can replace with the binding name of the element.
|
||||||
|
None => snippet(cx, elem.span, "..").into_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we
|
||||||
|
// can get from the `MethodCall`.
|
||||||
|
span_lint_hir_and_then(
|
||||||
|
cx,
|
||||||
|
UNUSED_ENUMERATE_INDEX,
|
||||||
|
recv_init_expr.hir_id,
|
||||||
|
enumerate_span,
|
||||||
|
"you seem to use `.enumerate()` and immediately discard the index",
|
||||||
|
|diag| {
|
||||||
|
multispan_sugg_with_applicability(
|
||||||
|
diag,
|
||||||
|
"remove the `.enumerate()` call",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
vec![
|
||||||
|
(closure_param.span, new_closure_param),
|
||||||
|
(
|
||||||
|
enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()),
|
||||||
|
String::new(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the span of the explicit type of the element.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// If the tuple argument:
|
||||||
|
/// * Has no explicit type, returns `None`
|
||||||
|
/// * Has an explicit tuple type with an implicit element type (`(usize, _)`), returns `None`
|
||||||
|
/// * Has an explicit tuple type with an explicit element type (`(_, i32)`), returns the span for
|
||||||
|
/// the element type.
|
||||||
|
fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option<Span> {
|
||||||
|
if let [tuple_ty] = fn_decl.inputs
|
||||||
|
&& let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind
|
||||||
|
&& !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer)
|
||||||
|
{
|
||||||
|
Some(elem_ty.span)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,8 @@
|
|||||||
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
|
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
|
||||||
pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
|
pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
|
||||||
pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
|
pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
|
||||||
|
pub const CORE_ITER_ENUMERATE_METHOD: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "enumerate"];
|
||||||
|
pub const CORE_ITER_ENUMERATE_STRUCT: [&str; 5] = ["core", "iter", "adapters", "enumerate", "Enumerate"];
|
||||||
pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
|
pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
|
||||||
pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"];
|
pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"];
|
||||||
pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
|
pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused, clippy::map_identity)]
|
||||||
#![warn(clippy::unused_enumerate_index)]
|
#![warn(clippy::unused_enumerate_index)]
|
||||||
|
|
||||||
use std::iter::Enumerate;
|
use std::iter::Enumerate;
|
||||||
|
|
||||||
|
fn get_enumerate() -> Enumerate<std::vec::IntoIter<i32>> {
|
||||||
|
vec![1].into_iter().enumerate()
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let v = [1, 2, 3];
|
let v = [1, 2, 3];
|
||||||
for x in v.iter() {
|
for x in v.iter() {
|
||||||
@ -55,4 +59,48 @@ fn main() {
|
|||||||
for x in dummy {
|
for x in dummy {
|
||||||
println!("{x}");
|
println!("{x}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = vec![1, 2, 3].into_iter().map(|x| println!("{x}"));
|
||||||
|
|
||||||
|
let p = vec![1, 2, 3].into_iter();
|
||||||
|
p.map(|x| println!("{x}"));
|
||||||
|
|
||||||
|
// This shouldn't trigger the lint. `get_enumerate` may come from an external library on which we
|
||||||
|
// have no control.
|
||||||
|
let p = get_enumerate();
|
||||||
|
p.map(|(_, x)| println!("{x}"));
|
||||||
|
|
||||||
|
// This shouldn't trigger the lint. The `enumerate` call is in a different context.
|
||||||
|
macro_rules! mac {
|
||||||
|
() => {
|
||||||
|
[1].iter().enumerate()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ = mac!().map(|(_, v)| v);
|
||||||
|
|
||||||
|
macro_rules! mac2 {
|
||||||
|
() => {
|
||||||
|
[1].iter()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ = mac2!().map(|_v| {});
|
||||||
|
|
||||||
|
// This shouldn't trigger the lint because of the `allow`.
|
||||||
|
#[allow(clippy::unused_enumerate_index)]
|
||||||
|
let v = [1].iter().enumerate();
|
||||||
|
v.map(|(_, _x)| {});
|
||||||
|
|
||||||
|
// This should keep the explicit type of `x`.
|
||||||
|
let v = [1, 2, 3].iter().copied();
|
||||||
|
let x = v.map(|x: i32| x).sum::<i32>();
|
||||||
|
assert_eq!(x, 6);
|
||||||
|
|
||||||
|
// This should keep the explicit type of `x`.
|
||||||
|
let v = [1, 2, 3].iter().copied();
|
||||||
|
let x = v.map(|x: i32| x).sum::<i32>();
|
||||||
|
assert_eq!(x, 6);
|
||||||
|
|
||||||
|
let v = [1, 2, 3].iter().copied();
|
||||||
|
let x = v.map(|x| x).sum::<i32>();
|
||||||
|
assert_eq!(x, 6);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused, clippy::map_identity)]
|
||||||
#![warn(clippy::unused_enumerate_index)]
|
#![warn(clippy::unused_enumerate_index)]
|
||||||
|
|
||||||
use std::iter::Enumerate;
|
use std::iter::Enumerate;
|
||||||
|
|
||||||
|
fn get_enumerate() -> Enumerate<std::vec::IntoIter<i32>> {
|
||||||
|
vec![1].into_iter().enumerate()
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let v = [1, 2, 3];
|
let v = [1, 2, 3];
|
||||||
for (_, x) in v.iter().enumerate() {
|
for (_, x) in v.iter().enumerate() {
|
||||||
@ -55,4 +59,48 @@ fn size_hint(&self) -> (usize, Option<usize>) {
|
|||||||
for (_, x) in dummy.enumerate() {
|
for (_, x) in dummy.enumerate() {
|
||||||
println!("{x}");
|
println!("{x}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}"));
|
||||||
|
|
||||||
|
let p = vec![1, 2, 3].into_iter().enumerate();
|
||||||
|
p.map(|(_, x)| println!("{x}"));
|
||||||
|
|
||||||
|
// This shouldn't trigger the lint. `get_enumerate` may come from an external library on which we
|
||||||
|
// have no control.
|
||||||
|
let p = get_enumerate();
|
||||||
|
p.map(|(_, x)| println!("{x}"));
|
||||||
|
|
||||||
|
// This shouldn't trigger the lint. The `enumerate` call is in a different context.
|
||||||
|
macro_rules! mac {
|
||||||
|
() => {
|
||||||
|
[1].iter().enumerate()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ = mac!().map(|(_, v)| v);
|
||||||
|
|
||||||
|
macro_rules! mac2 {
|
||||||
|
() => {
|
||||||
|
[1].iter()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ = mac2!().enumerate().map(|(_, _v)| {});
|
||||||
|
|
||||||
|
// This shouldn't trigger the lint because of the `allow`.
|
||||||
|
#[allow(clippy::unused_enumerate_index)]
|
||||||
|
let v = [1].iter().enumerate();
|
||||||
|
v.map(|(_, _x)| {});
|
||||||
|
|
||||||
|
// This should keep the explicit type of `x`.
|
||||||
|
let v = [1, 2, 3].iter().copied().enumerate();
|
||||||
|
let x = v.map(|(_, x): (usize, i32)| x).sum::<i32>();
|
||||||
|
assert_eq!(x, 6);
|
||||||
|
|
||||||
|
// This should keep the explicit type of `x`.
|
||||||
|
let v = [1, 2, 3].iter().copied().enumerate();
|
||||||
|
let x = v.map(|(_, x): (_, i32)| x).sum::<i32>();
|
||||||
|
assert_eq!(x, 6);
|
||||||
|
|
||||||
|
let v = [1, 2, 3].iter().copied().enumerate();
|
||||||
|
let x = v.map(|(_, x)| x).sum::<i32>();
|
||||||
|
assert_eq!(x, 6);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: you seem to use `.enumerate()` and immediately discard the index
|
error: you seem to use `.enumerate()` and immediately discard the index
|
||||||
--> tests/ui/unused_enumerate_index.rs:8:19
|
--> tests/ui/unused_enumerate_index.rs:12:19
|
||||||
|
|
|
|
||||||
LL | for (_, x) in v.iter().enumerate() {
|
LL | for (_, x) in v.iter().enumerate() {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -12,7 +12,7 @@ LL | for x in v.iter() {
|
|||||||
| ~ ~~~~~~~~
|
| ~ ~~~~~~~~
|
||||||
|
|
||||||
error: you seem to use `.enumerate()` and immediately discard the index
|
error: you seem to use `.enumerate()` and immediately discard the index
|
||||||
--> tests/ui/unused_enumerate_index.rs:55:19
|
--> tests/ui/unused_enumerate_index.rs:59:19
|
||||||
|
|
|
|
||||||
LL | for (_, x) in dummy.enumerate() {
|
LL | for (_, x) in dummy.enumerate() {
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
@ -22,5 +22,77 @@ help: remove the `.enumerate()` call
|
|||||||
LL | for x in dummy {
|
LL | for x in dummy {
|
||||||
| ~ ~~~~~
|
| ~ ~~~~~
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: you seem to use `.enumerate()` and immediately discard the index
|
||||||
|
--> tests/ui/unused_enumerate_index.rs:63:39
|
||||||
|
|
|
||||||
|
LL | let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}"));
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: remove the `.enumerate()` call
|
||||||
|
|
|
||||||
|
LL - let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}"));
|
||||||
|
LL + let _ = vec![1, 2, 3].into_iter().map(|x| println!("{x}"));
|
||||||
|
|
|
||||||
|
|
||||||
|
error: you seem to use `.enumerate()` and immediately discard the index
|
||||||
|
--> tests/ui/unused_enumerate_index.rs:65:39
|
||||||
|
|
|
||||||
|
LL | let p = vec![1, 2, 3].into_iter().enumerate();
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: remove the `.enumerate()` call
|
||||||
|
|
|
||||||
|
LL ~ let p = vec![1, 2, 3].into_iter();
|
||||||
|
LL ~ p.map(|x| println!("{x}"));
|
||||||
|
|
|
||||||
|
|
||||||
|
error: you seem to use `.enumerate()` and immediately discard the index
|
||||||
|
--> tests/ui/unused_enumerate_index.rs:86:17
|
||||||
|
|
|
||||||
|
LL | _ = mac2!().enumerate().map(|(_, _v)| {});
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: remove the `.enumerate()` call
|
||||||
|
|
|
||||||
|
LL - _ = mac2!().enumerate().map(|(_, _v)| {});
|
||||||
|
LL + _ = mac2!().map(|_v| {});
|
||||||
|
|
|
||||||
|
|
||||||
|
error: you seem to use `.enumerate()` and immediately discard the index
|
||||||
|
--> tests/ui/unused_enumerate_index.rs:94:39
|
||||||
|
|
|
||||||
|
LL | let v = [1, 2, 3].iter().copied().enumerate();
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: remove the `.enumerate()` call
|
||||||
|
|
|
||||||
|
LL ~ let v = [1, 2, 3].iter().copied();
|
||||||
|
LL ~ let x = v.map(|x: i32| x).sum::<i32>();
|
||||||
|
|
|
||||||
|
|
||||||
|
error: you seem to use `.enumerate()` and immediately discard the index
|
||||||
|
--> tests/ui/unused_enumerate_index.rs:99:39
|
||||||
|
|
|
||||||
|
LL | let v = [1, 2, 3].iter().copied().enumerate();
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: remove the `.enumerate()` call
|
||||||
|
|
|
||||||
|
LL ~ let v = [1, 2, 3].iter().copied();
|
||||||
|
LL ~ let x = v.map(|x: i32| x).sum::<i32>();
|
||||||
|
|
|
||||||
|
|
||||||
|
error: you seem to use `.enumerate()` and immediately discard the index
|
||||||
|
--> tests/ui/unused_enumerate_index.rs:103:39
|
||||||
|
|
|
||||||
|
LL | let v = [1, 2, 3].iter().copied().enumerate();
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: remove the `.enumerate()` call
|
||||||
|
|
|
||||||
|
LL ~ let v = [1, 2, 3].iter().copied();
|
||||||
|
LL ~ let x = v.map(|x| x).sum::<i32>();
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 8 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user