Skip to content

Commit 67ac73e

Browse files
borstesuji
authored andcommitted
Add option_and_then_some lint
1 parent d829d9f commit 67ac73e

File tree

8 files changed

+155
-2
lines changed

8 files changed

+155
-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

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 310 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 311 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/and_then_some.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use crate::utils::paths;
2+
use crate::utils::{in_macro_or_desugar, match_qpath, match_type, remove_blocks, snippet, span_lint_and_sugg};
3+
use rustc::hir;
4+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
5+
use rustc::{declare_lint_pass, declare_tool_lint};
6+
use rustc_errors::Applicability;
7+
8+
declare_clippy_lint! {
9+
/// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`.
10+
///
11+
/// **Why is this bad?** Readability, this can be written more concisely as
12+
/// `_.map(|x| y)`.
13+
///
14+
/// **Known problems:** None
15+
///
16+
/// **Example:**
17+
///
18+
/// ```rust
19+
/// let x = Some("foo");
20+
/// x.and_then(|s| Some(s.len()));
21+
/// ```
22+
///
23+
/// The correct use would be:
24+
///
25+
/// ```rust
26+
/// let x = Some("foo");
27+
/// x.map(|s| s.len());
28+
/// ```
29+
pub OPTION_AND_THEN_SOME,
30+
complexity,
31+
"using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
32+
}
33+
34+
declare_lint_pass!(AndThenSomeLint => [OPTION_AND_THEN_SOME]);
35+
36+
const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`";
37+
38+
impl LateLintPass<'_, '_> for AndThenSomeLint {
39+
/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
40+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
41+
if in_macro_or_desugar(e.span) {
42+
return;
43+
}
44+
45+
if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.node {
46+
if args.len() == 2 && method.ident.as_str() == "and_then" {
47+
let ty = cx.tables.expr_ty(&args[0]);
48+
if !match_type(cx, ty, &paths::OPTION) {
49+
return;
50+
}
51+
match args[1].node {
52+
hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
53+
let closure_body = cx.tcx.hir().body(body_id);
54+
let closure_expr = remove_blocks(&closure_body.value);
55+
56+
// let note = format!("{:?}", args[1]);
57+
// span_note_and_lint(cx, OPTION_AND_THEN_SOME, closure_args_span, "Outer debugging
58+
// information", closure_args_span, &note);
59+
60+
if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.node {
61+
if let hir::ExprKind::Path(ref qpath) = some_expr.node {
62+
if match_qpath(qpath, &paths::OPTION_SOME) {
63+
if some_args.len() == 1 {
64+
let inner_expr = &some_args[0];
65+
let some_inner_snip = snippet(cx, inner_expr.span, "..");
66+
let closure_args_snip = snippet(cx, closure_args_span, "..");
67+
let option_snip = snippet(cx, args[0].span, "..");
68+
let note =
69+
format!("{}.map({} {})", option_snip, closure_args_snip, some_inner_snip);
70+
span_lint_and_sugg(
71+
cx,
72+
OPTION_AND_THEN_SOME,
73+
e.span,
74+
LINT_MSG,
75+
"try this",
76+
note,
77+
Applicability::MachineApplicable,
78+
);
79+
}
80+
}
81+
}
82+
}
83+
},
84+
// hir::ExprKind::Path(ref qpath) => {
85+
// if match_qpath(qpath, &paths::OPTION_SOME) {
86+
// let note = format!("{:?}", args[1]);
87+
// span_note_and_lint(cx, OPTION_AND_THEN_SOME, args[1].span, "Some debugging information",
88+
// args[1].span, &note); }
89+
// },
90+
_ => {},
91+
}
92+
}
93+
}
94+
}
95+
}

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ mod consts;
145145
mod utils;
146146

147147
// begin lints modules, do not remove this comment, it’s used in `update_lints`
148+
pub mod and_then_some;
148149
pub mod approx_const;
149150
pub mod arithmetic;
150151
pub mod assertions_on_constants;
@@ -596,6 +597,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
596597
reg.register_late_lint_pass(box integer_division::IntegerDivision);
597598
reg.register_late_lint_pass(box inherent_to_string::InherentToString);
598599
reg.register_late_lint_pass(box trait_bounds::TraitBounds);
600+
reg.register_late_lint_pass(box and_then_some::AndThenSomeLint);
599601

600602
reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
601603
arithmetic::FLOAT_ARITHMETIC,
@@ -681,6 +683,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
681683
]);
682684

683685
reg.register_lint_group("clippy::all", Some("clippy"), vec![
686+
and_then_some::OPTION_AND_THEN_SOME,
684687
approx_const::APPROX_CONSTANT,
685688
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
686689
assign_ops::ASSIGN_OP_PATTERN,
@@ -1001,6 +1004,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
10011004
]);
10021005

10031006
reg.register_lint_group("clippy::complexity", Some("clippy_complexity"), vec![
1007+
and_then_some::OPTION_AND_THEN_SOME,
10041008
assign_ops::MISREFACTORED_ASSIGN_OP,
10051009
attrs::DEPRECATED_CFG_ATTR,
10061010
booleans::NONMINIMAL_BOOL,

src/lintlist/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 310] = [
9+
pub const ALL_LINTS: [Lint; 311] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -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: "and_then_some",
1325+
},
13191326
Lint {
13201327
name: "option_map_or_none",
13211328
group: "style",

tests/ui/option_and_then_some.fixed

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
#![deny(clippy::option_and_then_some)]
3+
4+
// need a main anyway, use it get rid of unused warnings too
5+
pub fn main() {
6+
let x = Some(5);
7+
// the easiest cases
8+
x.and_then(Some);
9+
x.map(|o| o + 1);
10+
// and an easy counter-example
11+
x.and_then(|o| if o < 32 { Some(o) } else { None });
12+
13+
// Different type
14+
let x: Result<u32, &str> = Ok(1);
15+
x.and_then(Ok);
16+
}

tests/ui/option_and_then_some.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
#![deny(clippy::option_and_then_some)]
3+
4+
// need a main anyway, use it get rid of unused warnings too
5+
pub fn main() {
6+
let x = Some(5);
7+
// the easiest cases
8+
x.and_then(Some);
9+
x.and_then(|o| Some(o + 1));
10+
// and an easy counter-example
11+
x.and_then(|o| if o < 32 { Some(o) } else { None });
12+
13+
// Different type
14+
let x: Result<u32, &str> = Ok(1);
15+
x.and_then(Ok);
16+
}

tests/ui/option_and_then_some.stderr

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
2+
--> $DIR/option_and_then_some.rs:9:5
3+
|
4+
LL | x.and_then(|o| Some(o + 1));
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)`
6+
|
7+
note: lint level defined here
8+
--> $DIR/option_and_then_some.rs:2:9
9+
|
10+
LL | #![deny(clippy::option_and_then_some)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)