Skip to content

Commit fe5cefc

Browse files
committed
Auto merge of rust-lang#7101 - camsteffen:flat-map-option, r=giraffate
Add flat_map_option lint changelog: Add flat_map_option lint Closes rust-lang#2241
2 parents c569d33 + 5af078a commit fe5cefc

File tree

9 files changed

+110
-3
lines changed

9 files changed

+110
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2216,6 +2216,7 @@ Released 2018-09-13
22162216
[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
22172217
[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
22182218
[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
2219+
[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
22192220
[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
22202221
[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
22212222
[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
770770
methods::FILTER_MAP_NEXT,
771771
methods::FILTER_NEXT,
772772
methods::FLAT_MAP_IDENTITY,
773+
methods::FLAT_MAP_OPTION,
773774
methods::FROM_ITER_INSTEAD_OF_COLLECT,
774775
methods::GET_UNWRAP,
775776
methods::IMPLICIT_CLONE,
@@ -1383,6 +1384,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13831384
LintId::of(matches::SINGLE_MATCH_ELSE),
13841385
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
13851386
LintId::of(methods::FILTER_MAP_NEXT),
1387+
LintId::of(methods::FLAT_MAP_OPTION),
13861388
LintId::of(methods::IMPLICIT_CLONE),
13871389
LintId::of(methods::INEFFICIENT_TO_STRING),
13881390
LintId::of(methods::MAP_FLATTEN),

clippy_lints/src/matches.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1509,7 +1509,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'
15091509
/// Gets all arms that are unbounded `PatRange`s.
15101510
fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
15111511
arms.iter()
1512-
.flat_map(|arm| {
1512+
.filter_map(|arm| {
15131513
if let Arm { pat, guard: None, .. } = *arm {
15141514
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
15151515
let lhs = match lhs {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_trait_method;
3+
use rustc_errors::Applicability;
4+
use rustc_hir as hir;
5+
use rustc_lint::LateContext;
6+
use rustc_middle::ty;
7+
use rustc_span::{source_map::Span, sym};
8+
9+
use super::FLAT_MAP_OPTION;
10+
use clippy_utils::ty::is_type_diagnostic_item;
11+
12+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
13+
if !is_trait_method(cx, expr, sym::Iterator) {
14+
return;
15+
}
16+
let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
17+
let sig = match arg_ty.kind() {
18+
ty::Closure(_, substs) => substs.as_closure().sig(),
19+
_ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx),
20+
_ => return,
21+
};
22+
if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::option_type) {
23+
return;
24+
}
25+
span_lint_and_sugg(
26+
cx,
27+
FLAT_MAP_OPTION,
28+
span,
29+
"used `flat_map` where `filter_map` could be used instead",
30+
"try",
31+
"filter_map".into(),
32+
Applicability::MachineApplicable,
33+
)
34+
}

clippy_lints/src/methods/mod.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod filter_map_identity;
1717
mod filter_map_next;
1818
mod filter_next;
1919
mod flat_map_identity;
20+
mod flat_map_option;
2021
mod from_iter_instead_of_collect;
2122
mod get_unwrap;
2223
mod implicit_clone;
@@ -97,6 +98,29 @@ declare_clippy_lint! {
9798
"used `cloned` where `copied` could be used instead"
9899
}
99100

101+
declare_clippy_lint! {
102+
/// **What it does:** Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
103+
/// used instead.
104+
///
105+
/// **Why is this bad?** When applicable, `filter_map()` is more clear since it shows that
106+
/// `Option` is used to produce 0 or 1 items.
107+
///
108+
/// **Known problems:** None.
109+
///
110+
/// **Example:**
111+
///
112+
/// ```rust
113+
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
114+
/// ```
115+
/// Use instead:
116+
/// ```rust
117+
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
118+
/// ```
119+
pub FLAT_MAP_OPTION,
120+
pedantic,
121+
"used `flat_map` where `filter_map` could be used instead"
122+
}
123+
100124
declare_clippy_lint! {
101125
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
102126
///
@@ -1663,6 +1687,7 @@ impl_lint_pass!(Methods => [
16631687
CLONE_ON_REF_PTR,
16641688
CLONE_DOUBLE_REF,
16651689
CLONED_INSTEAD_OF_COPIED,
1690+
FLAT_MAP_OPTION,
16661691
INEFFICIENT_TO_STRING,
16671692
NEW_RET_NO_SELF,
16681693
SINGLE_CHAR_PATTERN,
@@ -1958,7 +1983,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
19581983
unnecessary_filter_map::check(cx, expr, arg);
19591984
filter_map_identity::check(cx, expr, arg, span);
19601985
},
1961-
("flat_map", [flm_arg]) => flat_map_identity::check(cx, expr, flm_arg, span),
1986+
("flat_map", [arg]) => {
1987+
flat_map_identity::check(cx, expr, arg, span);
1988+
flat_map_option::check(cx, expr, arg, span);
1989+
},
19621990
("flatten", []) => {
19631991
if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
19641992
map_flatten::check(cx, expr, recv, map_arg);

clippy_utils/src/attrs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,6 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
154154
attrs
155155
.iter()
156156
.filter(|attr| attr.has_name(sym::doc))
157-
.flat_map(ast::Attribute::meta_item_list)
157+
.filter_map(ast::Attribute::meta_item_list)
158158
.any(|l| attr::list_contains_name(&l, sym::hidden))
159159
}

tests/ui/flat_map_option.fixed

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
#![warn(clippy::flat_map_option)]
3+
#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
4+
5+
fn main() {
6+
// yay
7+
let c = |x| Some(x);
8+
let _ = [1].iter().filter_map(c);
9+
let _ = [1].iter().filter_map(Some);
10+
11+
// nay
12+
let _ = [1].iter().flat_map(|_| &Some(1));
13+
}

tests/ui/flat_map_option.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
#![warn(clippy::flat_map_option)]
3+
#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
4+
5+
fn main() {
6+
// yay
7+
let c = |x| Some(x);
8+
let _ = [1].iter().flat_map(c);
9+
let _ = [1].iter().flat_map(Some);
10+
11+
// nay
12+
let _ = [1].iter().flat_map(|_| &Some(1));
13+
}

tests/ui/flat_map_option.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: used `flat_map` where `filter_map` could be used instead
2+
--> $DIR/flat_map_option.rs:8:24
3+
|
4+
LL | let _ = [1].iter().flat_map(c);
5+
| ^^^^^^^^ help: try: `filter_map`
6+
|
7+
= note: `-D clippy::flat-map-option` implied by `-D warnings`
8+
9+
error: used `flat_map` where `filter_map` could be used instead
10+
--> $DIR/flat_map_option.rs:9:24
11+
|
12+
LL | let _ = [1].iter().flat_map(Some);
13+
| ^^^^^^^^ help: try: `filter_map`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)