Skip to content

Commit cf64fc4

Browse files
committed
Move and_then_some to clippy_lints/src/methods/mod.rs
1 parent 02e0ab4 commit cf64fc4

File tree

6 files changed

+91
-2
lines changed

6 files changed

+91
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,7 @@ Released 2018-09-13
10881088
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
10891089
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
10901090
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
1091+
[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
10911092
[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
10921093
[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
10931094
[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
794794
methods::ITER_SKIP_NEXT,
795795
methods::NEW_RET_NO_SELF,
796796
methods::OK_EXPECT,
797+
methods::OPTION_AND_THEN_SOME,
797798
methods::OPTION_MAP_OR_NONE,
798799
methods::OR_FUN_CALL,
799800
methods::SEARCH_IS_SOME,
@@ -1032,6 +1033,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
10321033
methods::CLONE_ON_COPY,
10331034
methods::FILTER_NEXT,
10341035
methods::FLAT_MAP_IDENTITY,
1036+
methods::OPTION_AND_THEN_SOME,
10351037
methods::SEARCH_IS_SOME,
10361038
methods::UNNECESSARY_FILTER_MAP,
10371039
methods::USELESS_ASREF,

clippy_lints/src/methods/mod.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,32 @@ declare_clippy_lint! {
264264
"using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
265265
}
266266

267+
declare_clippy_lint! {
268+
/// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`.
269+
///
270+
/// **Why is this bad?** Readability, this can be written more concisely as
271+
/// `_.map(|x| y)`.
272+
///
273+
/// **Known problems:** None
274+
///
275+
/// **Example:**
276+
///
277+
/// ```rust
278+
/// let x = Some("foo");
279+
/// let _ = x.and_then(|s| Some(s.len()));
280+
/// ```
281+
///
282+
/// The correct use would be:
283+
///
284+
/// ```rust
285+
/// let x = Some("foo");
286+
/// let _ = x.map(|s| s.len());
287+
/// ```
288+
pub OPTION_AND_THEN_SOME,
289+
complexity,
290+
"using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
291+
}
292+
267293
declare_clippy_lint! {
268294
/// **What it does:** Checks for usage of `_.filter(_).next()`.
269295
///
@@ -900,6 +926,7 @@ declare_lint_pass!(Methods => [
900926
OPTION_MAP_UNWRAP_OR_ELSE,
901927
RESULT_MAP_UNWRAP_OR_ELSE,
902928
OPTION_MAP_OR_NONE,
929+
OPTION_AND_THEN_SOME,
903930
OR_FUN_CALL,
904931
EXPECT_FUN_CALL,
905932
CHARS_NEXT_CMP,
@@ -948,6 +975,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
948975
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0]),
949976
["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
950977
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
978+
["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),
951979
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
952980
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
953981
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
@@ -2052,6 +2080,57 @@ fn lint_map_or_none<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr,
20522080
}
20532081
}
20542082

2083+
/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
2084+
fn lint_option_and_then_some(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr]) {
2085+
let ty = cx.tables.expr_ty(&args[0]);
2086+
if !match_type(cx, ty, &paths::OPTION) {
2087+
return;
2088+
}
2089+
2090+
const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`";
2091+
2092+
match args[1].node {
2093+
hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
2094+
let closure_body = cx.tcx.hir().body(body_id);
2095+
let closure_expr = remove_blocks(&closure_body.value);
2096+
2097+
// let note = format!("{:?}", args[1]);
2098+
// span_note_and_lint(cx, OPTION_AND_THEN_SOME, closure_args_span, "Outer debugging
2099+
// information", closure_args_span, &note);
2100+
2101+
if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.node {
2102+
if let hir::ExprKind::Path(ref qpath) = some_expr.node {
2103+
if match_qpath(qpath, &paths::OPTION_SOME) {
2104+
if some_args.len() == 1 {
2105+
let inner_expr = &some_args[0];
2106+
let some_inner_snip = snippet(cx, inner_expr.span, "..");
2107+
let closure_args_snip = snippet(cx, closure_args_span, "..");
2108+
let option_snip = snippet(cx, args[0].span, "..");
2109+
let note = format!("{}.map({} {})", option_snip, closure_args_snip, some_inner_snip);
2110+
span_lint_and_sugg(
2111+
cx,
2112+
OPTION_AND_THEN_SOME,
2113+
expr.span,
2114+
LINT_MSG,
2115+
"try this",
2116+
note,
2117+
Applicability::MachineApplicable,
2118+
);
2119+
}
2120+
}
2121+
}
2122+
}
2123+
},
2124+
// hir::ExprKind::Path(ref qpath) => {
2125+
// if match_qpath(qpath, &paths::OPTION_SOME) {
2126+
// let note = format!("{:?}", args[1]);
2127+
// span_note_and_lint(cx, OPTION_AND_THEN_SOME, args[1].span, "Some debugging information",
2128+
// args[1].span, &note); }
2129+
// },
2130+
_ => {},
2131+
}
2132+
}
2133+
20552134
/// lint use of `filter().next()` for `Iterators`
20562135
fn lint_filter_next<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr, filter_args: &'tcx [hir::Expr]) {
20572136
// lint if caller of `.filter().next()` is an Iterator

src/lintlist/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,13 @@ pub const ALL_LINTS: [Lint; 310] = [
13161316
deprecation: None,
13171317
module: "eq_op",
13181318
},
1319+
Lint {
1320+
name: "option_and_then_some",
1321+
group: "complexity",
1322+
desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`",
1323+
deprecation: None,
1324+
module: "methods",
1325+
},
13191326
Lint {
13201327
name: "option_map_or_none",
13211328
group: "style",

tests/ui/option_and_then_some.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ pub fn main() {
1212

1313
// Different type
1414
let x: Result<u32, &str> = Ok(1);
15-
x.and_then(Ok);
15+
let _ = x.and_then(Ok);
1616
}

tests/ui/option_and_then_some.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ pub fn main() {
1212

1313
// Different type
1414
let x: Result<u32, &str> = Ok(1);
15-
x.and_then(Ok);
15+
let _ = x.and_then(Ok);
1616
}

0 commit comments

Comments
 (0)