Implement flat_map lint

This commit is contained in:
Jeremy Stucki 2019-06-24 15:08:26 +02:00
parent 1fd617d6df
commit c7da4c26fb
No known key found for this signature in database
GPG key ID: 8F548A5A2ED13F58
4 changed files with 55 additions and 0 deletions

View file

@ -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

View file

@ -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,

View file

@ -312,6 +312,26 @@ declare_clippy_lint! {
"using combination of `filter_map` and `next` which can usually be written as a single method call"
}
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(_)`.
///
@ -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>,

View file

@ -553,6 +553,13 @@ pub const ALL_LINTS: [Lint; 306] = [
deprecation: None,
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",