mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-12-03 18:09:42 +00:00
Auto merge of #7330 - xFrednet:0000-refactor-map-identity, r=flip1995
Refactoring identity function lints
I've noticed that we have several lints that all check for identity functions and each used their own check implementation. I moved the `is_expr_identity_function` function to `clippy_utils` and adapted all lints to reuse that one function. This should make the addition of new lints like this also easier in the future.
I've also moved the `map_identity` lint into the `methods` module. It's probably the best to review this PR by checking each commit individually. And that's it, have a great day 🙃
changelog: none
This commit is contained in:
commit
da0538eaca
13 changed files with 178 additions and 204 deletions
|
@ -254,7 +254,6 @@ mod manual_strip;
|
||||||
mod manual_unwrap_or;
|
mod manual_unwrap_or;
|
||||||
mod map_clone;
|
mod map_clone;
|
||||||
mod map_err_ignore;
|
mod map_err_ignore;
|
||||||
mod map_identity;
|
|
||||||
mod map_unit_fn;
|
mod map_unit_fn;
|
||||||
mod match_on_vec_items;
|
mod match_on_vec_items;
|
||||||
mod matches;
|
mod matches;
|
||||||
|
@ -705,7 +704,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
manual_unwrap_or::MANUAL_UNWRAP_OR,
|
manual_unwrap_or::MANUAL_UNWRAP_OR,
|
||||||
map_clone::MAP_CLONE,
|
map_clone::MAP_CLONE,
|
||||||
map_err_ignore::MAP_ERR_IGNORE,
|
map_err_ignore::MAP_ERR_IGNORE,
|
||||||
map_identity::MAP_IDENTITY,
|
|
||||||
map_unit_fn::OPTION_MAP_UNIT_FN,
|
map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||||
map_unit_fn::RESULT_MAP_UNIT_FN,
|
map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||||
match_on_vec_items::MATCH_ON_VEC_ITEMS,
|
match_on_vec_items::MATCH_ON_VEC_ITEMS,
|
||||||
|
@ -765,6 +763,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
methods::MANUAL_STR_REPEAT,
|
methods::MANUAL_STR_REPEAT,
|
||||||
methods::MAP_COLLECT_RESULT_UNIT,
|
methods::MAP_COLLECT_RESULT_UNIT,
|
||||||
methods::MAP_FLATTEN,
|
methods::MAP_FLATTEN,
|
||||||
|
methods::MAP_IDENTITY,
|
||||||
methods::MAP_UNWRAP_OR,
|
methods::MAP_UNWRAP_OR,
|
||||||
methods::NEW_RET_NO_SELF,
|
methods::NEW_RET_NO_SELF,
|
||||||
methods::OK_EXPECT,
|
methods::OK_EXPECT,
|
||||||
|
@ -1260,7 +1259,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
LintId::of(manual_strip::MANUAL_STRIP),
|
LintId::of(manual_strip::MANUAL_STRIP),
|
||||||
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
|
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
|
||||||
LintId::of(map_clone::MAP_CLONE),
|
LintId::of(map_clone::MAP_CLONE),
|
||||||
LintId::of(map_identity::MAP_IDENTITY),
|
|
||||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||||
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||||
|
@ -1301,6 +1299,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||||
LintId::of(methods::MANUAL_STR_REPEAT),
|
LintId::of(methods::MANUAL_STR_REPEAT),
|
||||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||||
|
LintId::of(methods::MAP_IDENTITY),
|
||||||
LintId::of(methods::NEW_RET_NO_SELF),
|
LintId::of(methods::NEW_RET_NO_SELF),
|
||||||
LintId::of(methods::OK_EXPECT),
|
LintId::of(methods::OK_EXPECT),
|
||||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||||
|
@ -1586,7 +1585,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
LintId::of(loops::WHILE_LET_LOOP),
|
LintId::of(loops::WHILE_LET_LOOP),
|
||||||
LintId::of(manual_strip::MANUAL_STRIP),
|
LintId::of(manual_strip::MANUAL_STRIP),
|
||||||
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
|
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
|
||||||
LintId::of(map_identity::MAP_IDENTITY),
|
|
||||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||||
LintId::of(matches::MATCH_AS_REF),
|
LintId::of(matches::MATCH_AS_REF),
|
||||||
|
@ -1601,6 +1599,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
LintId::of(methods::ITER_COUNT),
|
LintId::of(methods::ITER_COUNT),
|
||||||
LintId::of(methods::MANUAL_FILTER_MAP),
|
LintId::of(methods::MANUAL_FILTER_MAP),
|
||||||
LintId::of(methods::MANUAL_FIND_MAP),
|
LintId::of(methods::MANUAL_FIND_MAP),
|
||||||
|
LintId::of(methods::MAP_IDENTITY),
|
||||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||||
LintId::of(methods::OPTION_FILTER_MAP),
|
LintId::of(methods::OPTION_FILTER_MAP),
|
||||||
LintId::of(methods::SEARCH_IS_SOME),
|
LintId::of(methods::SEARCH_IS_SOME),
|
||||||
|
@ -2039,7 +2038,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
single_char_binding_names_threshold,
|
single_char_binding_names_threshold,
|
||||||
});
|
});
|
||||||
store.register_late_pass(|| box macro_use::MacroUseImports::default());
|
store.register_late_pass(|| box macro_use::MacroUseImports::default());
|
||||||
store.register_late_pass(|| box map_identity::MapIdentity);
|
|
||||||
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
|
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
|
||||||
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
|
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
|
||||||
store.register_late_pass(|| box repeat_once::RepeatOnce);
|
store.register_late_pass(|| box repeat_once::RepeatOnce);
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
|
||||||
use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
|
|
||||||
use if_chain::if_chain;
|
|
||||||
use rustc_errors::Applicability;
|
|
||||||
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|
||||||
use rustc_span::sym;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
|
||||||
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
|
|
||||||
///
|
|
||||||
/// **Why is this bad?** It can be written more concisely without the call to `map`.
|
|
||||||
///
|
|
||||||
/// **Known problems:** None.
|
|
||||||
///
|
|
||||||
/// **Example:**
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// let x = [1, 2, 3];
|
|
||||||
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
|
|
||||||
/// ```
|
|
||||||
/// Use instead:
|
|
||||||
/// ```rust
|
|
||||||
/// let x = [1, 2, 3];
|
|
||||||
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
|
|
||||||
/// ```
|
|
||||||
pub MAP_IDENTITY,
|
|
||||||
complexity,
|
|
||||||
"using iterator.map(|x| x)"
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_lint_pass!(MapIdentity => [MAP_IDENTITY]);
|
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for MapIdentity {
|
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|
||||||
if expr.span.from_expansion() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if_chain! {
|
|
||||||
if let Some([caller, func]) = get_map_argument(cx, expr);
|
|
||||||
if is_expr_identity_function(cx, func);
|
|
||||||
then {
|
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
MAP_IDENTITY,
|
|
||||||
expr.span.trim_start(caller.span).unwrap(),
|
|
||||||
"unnecessary map of the identity function",
|
|
||||||
"remove the call to `map`",
|
|
||||||
String::new(),
|
|
||||||
Applicability::MachineApplicable
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the arguments passed into map() if the expression is a method call to
|
|
||||||
/// map(). Otherwise, returns None.
|
|
||||||
fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
|
|
||||||
if_chain! {
|
|
||||||
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
|
|
||||||
if args.len() == 2 && method.ident.name == sym::map;
|
|
||||||
let caller_ty = cx.typeck_results().expr_ty(&args[0]);
|
|
||||||
if is_trait_method(cx, expr, sym::Iterator)
|
|
||||||
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|
|
||||||
|| is_type_diagnostic_item(cx, caller_ty, sym::option_type);
|
|
||||||
then {
|
|
||||||
Some(args)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if an expression represents the identity function
|
|
||||||
/// Only examines closures and `std::convert::identity`
|
|
||||||
fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|
||||||
match expr.kind {
|
|
||||||
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
|
|
||||||
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if a function's body represents the identity function
|
|
||||||
/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
|
|
||||||
/// return x; }`
|
|
||||||
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
|
||||||
let params = func.params;
|
|
||||||
let body = remove_blocks(&func.value);
|
|
||||||
|
|
||||||
// if there's less/more than one parameter, then it is not the identity function
|
|
||||||
if params.len() != 1 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match body.kind {
|
|
||||||
ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
|
|
||||||
ExprKind::Ret(Some(ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
|
|
||||||
ExprKind::Block(block, _) => {
|
|
||||||
if_chain! {
|
|
||||||
if block.stmts.len() == 1;
|
|
||||||
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind;
|
|
||||||
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
|
|
||||||
then {
|
|
||||||
match_expr_param(cx, ret_val, params[0].pat)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true iff an expression returns the same thing as a parameter's pattern
|
|
||||||
fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
|
|
||||||
if let PatKind::Binding(_, _, ident, _) = pat.kind {
|
|
||||||
match_var(expr, ident.name) && !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
|
use clippy_utils::{is_expr_identity_function, is_trait_method};
|
||||||
use if_chain::if_chain;
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
@ -9,32 +8,15 @@ use rustc_span::{source_map::Span, sym};
|
||||||
use super::FILTER_MAP_IDENTITY;
|
use super::FILTER_MAP_IDENTITY;
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
|
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
|
||||||
if is_trait_method(cx, expr, sym::Iterator) {
|
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
|
||||||
let apply_lint = |message: &str| {
|
span_lint_and_sugg(
|
||||||
span_lint_and_sugg(
|
cx,
|
||||||
cx,
|
FILTER_MAP_IDENTITY,
|
||||||
FILTER_MAP_IDENTITY,
|
filter_map_span.with_hi(expr.span.hi()),
|
||||||
filter_map_span.with_hi(expr.span.hi()),
|
"use of `filter_map` with an identity function",
|
||||||
message,
|
"try",
|
||||||
"try",
|
"flatten()".to_string(),
|
||||||
"flatten()".to_string(),
|
Applicability::MachineApplicable,
|
||||||
Applicability::MachineApplicable,
|
);
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if_chain! {
|
|
||||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind;
|
|
||||||
let body = cx.tcx.hir().body(body_id);
|
|
||||||
|
|
||||||
if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind;
|
|
||||||
if path_to_local_id(&body.value, binding_id);
|
|
||||||
then {
|
|
||||||
apply_lint("called `filter_map(|x| x)` on an `Iterator`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
|
|
||||||
apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
|
use clippy_utils::{is_expr_identity_function, is_trait_method};
|
||||||
use if_chain::if_chain;
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
@ -15,36 +14,15 @@ pub(super) fn check<'tcx>(
|
||||||
flat_map_arg: &'tcx hir::Expr<'_>,
|
flat_map_arg: &'tcx hir::Expr<'_>,
|
||||||
flat_map_span: Span,
|
flat_map_span: Span,
|
||||||
) {
|
) {
|
||||||
if is_trait_method(cx, expr, sym::Iterator) {
|
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
|
||||||
let apply_lint = |message: &str| {
|
span_lint_and_sugg(
|
||||||
span_lint_and_sugg(
|
cx,
|
||||||
cx,
|
FLAT_MAP_IDENTITY,
|
||||||
FLAT_MAP_IDENTITY,
|
flat_map_span.with_hi(expr.span.hi()),
|
||||||
flat_map_span.with_hi(expr.span.hi()),
|
"use of `flat_map` with an identity function",
|
||||||
message,
|
"try",
|
||||||
"try",
|
"flatten()".to_string(),
|
||||||
"flatten()".to_string(),
|
Applicability::MachineApplicable,
|
||||||
Applicability::MachineApplicable,
|
);
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if_chain! {
|
|
||||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind;
|
|
||||||
let body = cx.tcx.hir().body(body_id);
|
|
||||||
|
|
||||||
if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
|
|
||||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
|
|
||||||
|
|
||||||
if path.segments.len() == 1;
|
|
||||||
if path.segments[0].ident.name == binding_ident.name;
|
|
||||||
|
|
||||||
then {
|
|
||||||
apply_lint("called `flat_map(|x| x)` on an `Iterator`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
|
|
||||||
apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
clippy_lints/src/methods/map_identity.rs
Normal file
38
clippy_lints/src/methods/map_identity.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
|
use clippy_utils::{is_expr_identity_function, is_trait_method};
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::{source_map::Span, sym};
|
||||||
|
|
||||||
|
use super::MAP_IDENTITY;
|
||||||
|
|
||||||
|
pub(super) fn check(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &hir::Expr<'_>,
|
||||||
|
caller: &hir::Expr<'_>,
|
||||||
|
map_arg: &hir::Expr<'_>,
|
||||||
|
_map_span: Span,
|
||||||
|
) {
|
||||||
|
let caller_ty = cx.typeck_results().expr_ty(caller);
|
||||||
|
|
||||||
|
if_chain! {
|
||||||
|
if is_trait_method(cx, expr, sym::Iterator)
|
||||||
|
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|
||||||
|
|| is_type_diagnostic_item(cx, caller_ty, sym::option_type);
|
||||||
|
if is_expr_identity_function(cx, map_arg);
|
||||||
|
if let Some(sugg_span) = expr.span.trim_start(caller.span);
|
||||||
|
then {
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
MAP_IDENTITY,
|
||||||
|
sugg_span,
|
||||||
|
"unnecessary map of the identity function",
|
||||||
|
"remove the call to `map`",
|
||||||
|
String::new(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ mod manual_saturating_arithmetic;
|
||||||
mod manual_str_repeat;
|
mod manual_str_repeat;
|
||||||
mod map_collect_result_unit;
|
mod map_collect_result_unit;
|
||||||
mod map_flatten;
|
mod map_flatten;
|
||||||
|
mod map_identity;
|
||||||
mod map_unwrap_or;
|
mod map_unwrap_or;
|
||||||
mod ok_expect;
|
mod ok_expect;
|
||||||
mod option_as_ref_deref;
|
mod option_as_ref_deref;
|
||||||
|
@ -1561,6 +1562,29 @@ declare_clippy_lint! {
|
||||||
"call to `filter_map` where `flatten` is sufficient"
|
"call to `filter_map` where `flatten` is sufficient"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
|
||||||
|
///
|
||||||
|
/// **Why is this bad?** It can be written more concisely without the call to `map`.
|
||||||
|
///
|
||||||
|
/// **Known problems:** None.
|
||||||
|
///
|
||||||
|
/// **Example:**
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let x = [1, 2, 3];
|
||||||
|
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// let x = [1, 2, 3];
|
||||||
|
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
|
||||||
|
/// ```
|
||||||
|
pub MAP_IDENTITY,
|
||||||
|
complexity,
|
||||||
|
"using iterator.map(|x| x)"
|
||||||
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// **What it does:** Checks for the use of `.bytes().nth()`.
|
/// **What it does:** Checks for the use of `.bytes().nth()`.
|
||||||
///
|
///
|
||||||
|
@ -1728,6 +1752,7 @@ impl_lint_pass!(Methods => [
|
||||||
FILTER_NEXT,
|
FILTER_NEXT,
|
||||||
SKIP_WHILE_NEXT,
|
SKIP_WHILE_NEXT,
|
||||||
FILTER_MAP_IDENTITY,
|
FILTER_MAP_IDENTITY,
|
||||||
|
MAP_IDENTITY,
|
||||||
MANUAL_FILTER_MAP,
|
MANUAL_FILTER_MAP,
|
||||||
MANUAL_FIND_MAP,
|
MANUAL_FIND_MAP,
|
||||||
OPTION_FILTER_MAP,
|
OPTION_FILTER_MAP,
|
||||||
|
@ -2058,6 +2083,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
map_identity::check(cx, expr, recv, m_arg, span);
|
||||||
},
|
},
|
||||||
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
|
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
|
||||||
("next", []) => {
|
("next", []) => {
|
||||||
|
|
|
@ -1401,6 +1401,60 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
|
did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if an expression represents the identity function
|
||||||
|
/// Only examines closures and `std::convert::identity`
|
||||||
|
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
|
/// Returns true if the expression is a binding to the given pattern
|
||||||
|
fn is_expr_pat_binding(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
|
||||||
|
if let PatKind::Binding(_, _, ident, _) = pat.kind {
|
||||||
|
if match_var(expr, ident.name) {
|
||||||
|
return !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
|
||||||
|
/// * `|x| x`
|
||||||
|
/// * `|x| return x`
|
||||||
|
/// * `|x| { return x }`
|
||||||
|
/// * `|x| { return x; }`
|
||||||
|
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
||||||
|
let body = remove_blocks(&func.value);
|
||||||
|
|
||||||
|
let value_pat = if let [value_param] = func.params {
|
||||||
|
value_param.pat
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
match body.kind {
|
||||||
|
ExprKind::Path(QPath::Resolved(None, _)) => is_expr_pat_binding(cx, body, value_pat),
|
||||||
|
ExprKind::Ret(Some(ret_val)) => is_expr_pat_binding(cx, ret_val, value_pat),
|
||||||
|
ExprKind::Block(block, _) => {
|
||||||
|
if_chain! {
|
||||||
|
if let &[block_stmt] = &block.stmts;
|
||||||
|
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block_stmt.kind;
|
||||||
|
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
|
||||||
|
then {
|
||||||
|
is_expr_pat_binding(cx, ret_val, value_pat)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
|
||||||
|
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the node where an expression is either used, or it's type is unified with another branch.
|
/// Gets the node where an expression is either used, or it's type is unified with another branch.
|
||||||
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
|
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
|
||||||
let map = tcx.hir();
|
let map = tcx.hir();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports, clippy::needless_return)]
|
||||||
#![warn(clippy::filter_map_identity)]
|
#![warn(clippy::filter_map_identity)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -13,4 +13,7 @@ fn main() {
|
||||||
use std::convert::identity;
|
use std::convert::identity;
|
||||||
let iterator = vec![Some(1), None, Some(2)].into_iter();
|
let iterator = vec![Some(1), None, Some(2)].into_iter();
|
||||||
let _ = iterator.flatten();
|
let _ = iterator.flatten();
|
||||||
|
|
||||||
|
let iterator = vec![Some(1), None, Some(2)].into_iter();
|
||||||
|
let _ = iterator.flatten();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports, clippy::needless_return)]
|
||||||
#![warn(clippy::filter_map_identity)]
|
#![warn(clippy::filter_map_identity)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -13,4 +13,7 @@ fn main() {
|
||||||
use std::convert::identity;
|
use std::convert::identity;
|
||||||
let iterator = vec![Some(1), None, Some(2)].into_iter();
|
let iterator = vec![Some(1), None, Some(2)].into_iter();
|
||||||
let _ = iterator.filter_map(identity);
|
let _ = iterator.filter_map(identity);
|
||||||
|
|
||||||
|
let iterator = vec![Some(1), None, Some(2)].into_iter();
|
||||||
|
let _ = iterator.filter_map(|x| return x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error: called `filter_map(|x| x)` on an `Iterator`
|
error: use of `filter_map` with an identity function
|
||||||
--> $DIR/filter_map_identity.rs:8:22
|
--> $DIR/filter_map_identity.rs:8:22
|
||||||
|
|
|
|
||||||
LL | let _ = iterator.filter_map(|x| x);
|
LL | let _ = iterator.filter_map(|x| x);
|
||||||
|
@ -6,17 +6,23 @@ LL | let _ = iterator.filter_map(|x| x);
|
||||||
|
|
|
|
||||||
= note: `-D clippy::filter-map-identity` implied by `-D warnings`
|
= note: `-D clippy::filter-map-identity` implied by `-D warnings`
|
||||||
|
|
||||||
error: called `filter_map(std::convert::identity)` on an `Iterator`
|
error: use of `filter_map` with an identity function
|
||||||
--> $DIR/filter_map_identity.rs:11:22
|
--> $DIR/filter_map_identity.rs:11:22
|
||||||
|
|
|
|
||||||
LL | let _ = iterator.filter_map(std::convert::identity);
|
LL | let _ = iterator.filter_map(std::convert::identity);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
|
||||||
|
|
||||||
error: called `filter_map(std::convert::identity)` on an `Iterator`
|
error: use of `filter_map` with an identity function
|
||||||
--> $DIR/filter_map_identity.rs:15:22
|
--> $DIR/filter_map_identity.rs:15:22
|
||||||
|
|
|
|
||||||
LL | let _ = iterator.filter_map(identity);
|
LL | let _ = iterator.filter_map(identity);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
|
| ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: use of `filter_map` with an identity function
|
||||||
|
--> $DIR/filter_map_identity.rs:18:22
|
||||||
|
|
|
||||||
|
LL | let _ = iterator.filter_map(|x| return x);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports, clippy::needless_return)]
|
||||||
#![warn(clippy::flat_map_identity)]
|
#![warn(clippy::flat_map_identity)]
|
||||||
|
|
||||||
use std::convert;
|
use std::convert;
|
||||||
|
@ -11,4 +11,7 @@ fn main() {
|
||||||
|
|
||||||
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
|
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
|
||||||
let _ = iterator.flatten();
|
let _ = iterator.flatten();
|
||||||
|
|
||||||
|
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
|
||||||
|
let _ = iterator.flatten();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports, clippy::needless_return)]
|
||||||
#![warn(clippy::flat_map_identity)]
|
#![warn(clippy::flat_map_identity)]
|
||||||
|
|
||||||
use std::convert;
|
use std::convert;
|
||||||
|
@ -11,4 +11,7 @@ fn main() {
|
||||||
|
|
||||||
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
|
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
|
||||||
let _ = iterator.flat_map(convert::identity);
|
let _ = iterator.flat_map(convert::identity);
|
||||||
|
|
||||||
|
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
|
||||||
|
let _ = iterator.flat_map(|x| return x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error: called `flat_map(|x| x)` on an `Iterator`
|
error: use of `flat_map` with an identity function
|
||||||
--> $DIR/flat_map_identity.rs:10:22
|
--> $DIR/flat_map_identity.rs:10:22
|
||||||
|
|
|
|
||||||
LL | let _ = iterator.flat_map(|x| x);
|
LL | let _ = iterator.flat_map(|x| x);
|
||||||
|
@ -6,11 +6,17 @@ LL | let _ = iterator.flat_map(|x| x);
|
||||||
|
|
|
|
||||||
= note: `-D clippy::flat-map-identity` implied by `-D warnings`
|
= note: `-D clippy::flat-map-identity` implied by `-D warnings`
|
||||||
|
|
||||||
error: called `flat_map(std::convert::identity)` on an `Iterator`
|
error: use of `flat_map` with an identity function
|
||||||
--> $DIR/flat_map_identity.rs:13:22
|
--> $DIR/flat_map_identity.rs:13:22
|
||||||
|
|
|
|
||||||
LL | let _ = iterator.flat_map(convert::identity);
|
LL | let _ = iterator.flat_map(convert::identity);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: use of `flat_map` with an identity function
|
||||||
|
--> $DIR/flat_map_identity.rs:16:22
|
||||||
|
|
|
||||||
|
LL | let _ = iterator.flat_map(|x| return x);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue