about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJeremy Stucki <stucki.jeremy@gmail.com>2019-06-24 15:08:26 +0200
committerJeremy Stucki <stucki.jeremy@gmail.com>2019-07-08 17:11:54 +0200
commitc7da4c26fbff41c07fe03927847f3fe233d8b5ad (patch)
treef7cc7571aa02ee2ecf94c7f019cf276f8672e53a
parent1fd617d6df6055516b5cc4b265037e4188806d1d (diff)
downloadrust-c7da4c26fbff41c07fe03927847f3fe233d8b5ad.tar.gz
rust-c7da4c26fbff41c07fe03927847f3fe233d8b5ad.zip
Implement flat_map lint
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/lib.rs1
-rw-r--r--clippy_lints/src/methods/mod.rs46
-rw-r--r--src/lintlist/mod.rs7
4 files changed, 55 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 461adb729f9..b907b7f0e79 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -947,6 +947,7 @@ Released 2018-09-13
 [`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 [`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 [`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
+[`flat_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map
 [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 0d2c9e6b987..5548bb0ab62 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -637,6 +637,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
         methods::FILTER_MAP,
         methods::FILTER_MAP_NEXT,
         methods::FIND_MAP,
+        methods::FLAT_MAP,
         methods::MAP_FLATTEN,
         methods::OPTION_MAP_UNWRAP_OR,
         methods::OPTION_MAP_UNWRAP_OR_ELSE,
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index f689a2d4ef0..1578976ed85 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -313,6 +313,26 @@ declare_clippy_lint! {
 }
 
 declare_clippy_lint! {
+    /// **What it does:** Checks for usage of `flat_map(|x| x)`.
+    ///
+    /// **Why is this bad?** Readability, this can be written more concisely by using `flatten`.
+    ///
+    /// **Known problems:** None
+    ///
+    /// **Example:**
+    /// ```rust
+    /// iter.flat_map(|x| x)
+    /// ```
+    /// Can be written as
+    /// ```rust
+    /// iter.flatten()
+    /// ```
+    pub FLAT_MAP,
+    pedantic,
+    "call to `flat_map` where `flatten` is sufficient"
+}
+
+declare_clippy_lint! {
     /// **What it does:** Checks for usage of `_.find(_).map(_)`.
     ///
     /// **Why is this bad?** Readability, this can be written more concisely as a
@@ -844,6 +864,7 @@ declare_lint_pass!(Methods => [
     FILTER_NEXT,
     FILTER_MAP,
     FILTER_MAP_NEXT,
+    FLAT_MAP,
     FIND_MAP,
     MAP_FLATTEN,
     ITER_NTH,
@@ -884,6 +905,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
             ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
             ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
             ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
+            ["flat_map", ..] => lint_flat_map(cx, expr, arg_lists[0]),
             ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]),
             ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0]),
             ["is_some", "position"] => lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0]),
@@ -2092,6 +2114,30 @@ fn lint_filter_map_flat_map<'a, 'tcx>(
     }
 }
 
+/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient
+fn lint_flat_map<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr, flat_map_args: &'tcx [hir::Expr]) {
+    if_chain! {
+        if match_trait_method(cx, expr, &paths::ITERATOR);
+
+        if flat_map_args.len() == 2;
+        if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_args[1].node;
+        let body = cx.tcx.hir().body(body_id);
+
+        if body.arguments.len() == 1;
+        if let hir::PatKind::Binding(_, _, binding_ident, _) = body.arguments[0].pat.node;
+        if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.node;
+
+        if path.segments.len() == 1;
+        if path.segments[0].ident.as_str() == binding_ident.as_str();
+
+        then {
+            let msg = "called `flat_map(|x| x)` on an `Iterator`. \
+                       This can be simplified by calling `flatten().`";
+            span_lint(cx, FLAT_MAP, expr.span, msg);
+        }
+    }
+}
+
 /// lint searching an Iterator followed by `is_some()`
 fn lint_search_is_some<'a, 'tcx>(
     cx: &LateContext<'a, 'tcx>,
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index 8c49a3b1838..a7727258b53 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -554,6 +554,13 @@ pub const ALL_LINTS: [Lint; 306] = [
         module: "methods",
     },
     Lint {
+        name: "flat_map",
+        group: "pedantic",
+        desc: "call to `flat_map` where `flatten` is sufficient",
+        deprecation: None,
+        module: "methods",
+    },
+    Lint {
         name: "float_arithmetic",
         group: "restriction",
         desc: "any floating-point arithmetic statement",