|
1 |
| -use matches::matches; |
2 | 1 | use crate::rustc::hir;
|
3 |
| -use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass, in_external_macro, Lint, LintContext}; |
| 2 | +use crate::rustc::hir::def::Def; |
| 3 | +use crate::rustc::lint::{in_external_macro, LateContext, LateLintPass, Lint, LintArray, LintContext, LintPass}; |
| 4 | +use crate::rustc::ty::{self, Ty}; |
4 | 5 | use crate::rustc::{declare_tool_lint, lint_array};
|
| 6 | +use crate::rustc_errors::Applicability; |
| 7 | +use crate::syntax::ast; |
| 8 | +use crate::syntax::source_map::{BytePos, Span}; |
| 9 | +use crate::utils::paths; |
| 10 | +use crate::utils::sugg; |
| 11 | +use crate::utils::{ |
| 12 | + get_arg_name, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, is_self, is_self_ty, |
| 13 | + iter_input_pats, last_path_segment, match_def_path, match_path, match_qpath, match_trait_method, match_type, |
| 14 | + match_var, method_chain_args, remove_blocks, return_ty, same_tys, single_segment_path, snippet, span_lint, |
| 15 | + span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, |
| 16 | +}; |
5 | 17 | use if_chain::if_chain;
|
6 |
| -use crate::rustc::ty::{self, Ty}; |
7 |
| -use crate::rustc::hir::def::Def; |
| 18 | +use matches::matches; |
8 | 19 | use std::borrow::Cow;
|
9 | 20 | use std::fmt;
|
10 | 21 | use std::iter;
|
11 |
| -use crate::syntax::ast; |
12 |
| -use crate::syntax::source_map::{Span, BytePos}; |
13 |
| -use crate::utils::{get_arg_name, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, is_self, |
14 |
| - is_self_ty, iter_input_pats, last_path_segment, match_def_path, match_path, match_qpath, match_trait_method, |
15 |
| - match_type, method_chain_args, match_var, return_ty, remove_blocks, same_tys, single_segment_path, snippet, |
16 |
| - span_lint, span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq}; |
17 |
| -use crate::utils::paths; |
18 |
| -use crate::utils::sugg; |
19 |
| -use crate::rustc_errors::Applicability; |
20 | 22 |
|
21 | 23 | #[derive(Clone)]
|
22 | 24 | pub struct Pass;
|
@@ -247,6 +249,24 @@ declare_clippy_lint! {
|
247 | 249 | "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
|
248 | 250 | }
|
249 | 251 |
|
| 252 | +/// **What it does:** Checks for usage of `_.map(_).flatten(_)`, |
| 253 | +/// |
| 254 | +/// **Why is this bad?** Readability, this can be written more concisely as a |
| 255 | +/// single method call. |
| 256 | +/// |
| 257 | +/// **Known problems:** |
| 258 | +/// |
| 259 | +/// **Example:** |
| 260 | +/// ```rust |
| 261 | +/// iter.map(|x| x.iter()).flatten() |
| 262 | +/// ``` |
| 263 | +declare_clippy_lint! { |
| 264 | + pub MAP_FLATTEN, |
| 265 | + pedantic, |
| 266 | + "using combinations of `flatten` and `map` which can usually be written as a \ |
| 267 | + single method call" |
| 268 | +} |
| 269 | + |
250 | 270 | /// **What it does:** Checks for usage of `_.filter(_).map(_)`,
|
251 | 271 | /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
|
252 | 272 | ///
|
@@ -698,6 +718,7 @@ impl LintPass for Pass {
|
698 | 718 | TEMPORARY_CSTRING_AS_PTR,
|
699 | 719 | FILTER_NEXT,
|
700 | 720 | FILTER_MAP,
|
| 721 | + MAP_FLATTEN, |
701 | 722 | ITER_NTH,
|
702 | 723 | ITER_SKIP_NEXT,
|
703 | 724 | GET_UNWRAP,
|
@@ -744,6 +765,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
744 | 765 | lint_filter_flat_map(cx, expr, arglists[0], arglists[1]);
|
745 | 766 | } else if let Some(arglists) = method_chain_args(expr, &["filter_map", "flat_map"]) {
|
746 | 767 | lint_filter_map_flat_map(cx, expr, arglists[0], arglists[1]);
|
| 768 | + } else if let Some(arglists) = method_chain_args(expr, &["map", "flatten"]) { |
| 769 | + lint_map_flatten(cx, expr, arglists[0]); |
747 | 770 | } else if let Some(arglists) = method_chain_args(expr, &["find", "is_some"]) {
|
748 | 771 | lint_search_is_some(cx, expr, "find", arglists[0], arglists[1]);
|
749 | 772 | } else if let Some(arglists) = method_chain_args(expr, &["position", "is_some"]) {
|
@@ -1577,6 +1600,30 @@ fn lint_map_unwrap_or(cx: &LateContext<'_, '_>, expr: &hir::Expr, map_args: &[hi
|
1577 | 1600 | }
|
1578 | 1601 | }
|
1579 | 1602 |
|
| 1603 | +/// lint use of `map().flatten()` for `Iterators` |
| 1604 | +fn lint_map_flatten<'a, 'tcx>( |
| 1605 | + cx: &LateContext<'a, 'tcx>, |
| 1606 | + expr: &'tcx hir::Expr, |
| 1607 | + map_args: &'tcx [hir::Expr], |
| 1608 | +) { |
| 1609 | + // lint if caller of `.map().flatten()` is an Iterator |
| 1610 | + if match_trait_method(cx, expr, &paths::ITERATOR) { |
| 1611 | + let msg = "called `map(..).flatten()` on an `Iterator`. \ |
| 1612 | + This is more succinctly expressed by calling `.flat_map(..)`"; |
| 1613 | + let self_snippet = snippet(cx, map_args[0].span, ".."); |
| 1614 | + let func_snippet = snippet(cx, map_args[1].span, ".."); |
| 1615 | + let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); |
| 1616 | + span_lint_and_then(cx, MAP_FLATTEN, expr.span, msg, |db| { |
| 1617 | + db.span_suggestion_with_applicability( |
| 1618 | + expr.span, |
| 1619 | + "try using flat_map instead", |
| 1620 | + hint, |
| 1621 | + Applicability::MachineApplicable, |
| 1622 | + ); |
| 1623 | + }); |
| 1624 | + } |
| 1625 | +} |
| 1626 | + |
1580 | 1627 | /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
|
1581 | 1628 | fn lint_map_unwrap_or_else<'a, 'tcx>(
|
1582 | 1629 | cx: &LateContext<'a, 'tcx>,
|
|
0 commit comments