Skip to content

Commit 12e7cfa

Browse files
author
Yusuf Raji
committed
Add single_option_map lint
1 parent 10045db commit 12e7cfa

6 files changed

Lines changed: 110 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6060,6 +6060,7 @@ Released 2018-09-13
60606060
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
60616061
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
60626062
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
6063+
[`single_option_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_option_map
60636064
[`single_range_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_range_in_vec_init
60646065
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
60656066
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
677677
crate::single_call_fn::SINGLE_CALL_FN_INFO,
678678
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
679679
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
680+
crate::single_option_map::SINGLE_OPTION_MAP_INFO,
680681
crate::single_range_in_vec_init::SINGLE_RANGE_IN_VEC_INIT_INFO,
681682
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
682683
crate::size_of_ref::SIZE_OF_REF_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ mod significant_drop_tightening;
337337
mod single_call_fn;
338338
mod single_char_lifetime_names;
339339
mod single_component_path_imports;
340+
mod single_option_map;
340341
mod single_range_in_vec_init;
341342
mod size_of_in_element_count;
342343
mod size_of_ref;
@@ -972,5 +973,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
972973
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
973974
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
974975
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
976+
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
975977
// add lints here, do not remove this comment, it's used in `new_lint`
976978
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::peel_blocks;
3+
use clippy_utils::ty::is_type_diagnostic_item;
4+
use rustc_hir::def_id::LocalDefId;
5+
use rustc_hir::intravisit::FnKind;
6+
use rustc_hir::{Body, ExprKind, FnDecl, FnRetTy};
7+
use rustc_lint::{LateContext, LateLintPass};
8+
use rustc_session::declare_lint_pass;
9+
use rustc_span::{Span, sym};
10+
11+
declare_clippy_lint! {
12+
/// ### What it does
13+
/// Checks for functions with method calls to `.map(_)` on an arg
14+
/// of type `Option` as the outermost expression.
15+
///
16+
/// ### Why is this bad?
17+
/// Taking and returning an `Option<T>` may require additional
18+
/// `Some(_)` and `unwrap` if all you have is a `T`
19+
///
20+
/// ### Example
21+
/// ```no_run
22+
/// fn double(param: Option<u32>) -> Option<u32> {
23+
/// param.map(|x| x * 2)
24+
/// }
25+
/// ```
26+
/// Use instead:
27+
/// ```no_run
28+
/// fn double(param: u32) -> u32 {
29+
/// param * 2
30+
/// }
31+
/// ```
32+
#[clippy::version = "1.86.0"]
33+
pub SINGLE_OPTION_MAP,
34+
nursery,
35+
"Checks for functions with method calls to `.map(_)` on an arg of type `Option` as the outermost expression."
36+
}
37+
38+
declare_lint_pass!(SingleOptionMap => [SINGLE_OPTION_MAP]);
39+
40+
impl<'tcx> LateLintPass<'tcx> for SingleOptionMap {
41+
fn check_fn(
42+
&mut self,
43+
cx: &LateContext<'tcx>,
44+
kind: FnKind<'tcx>,
45+
decl: &'tcx FnDecl<'tcx>,
46+
body: &'tcx Body<'tcx>,
47+
span: Span,
48+
_fn_def: LocalDefId,
49+
) {
50+
if let FnRetTy::Return(_ret) = decl.output
51+
&& matches!(kind, FnKind::ItemFn(_, _, _) | FnKind::Method(_, _))
52+
{
53+
let func_body = peel_blocks(body.value);
54+
if let ExprKind::MethodCall(method_name, callee, _args, _span) = func_body.kind
55+
&& method_name.ident.name == sym::map
56+
&& let callee_type = cx.typeck_results().expr_ty(callee)
57+
&& is_type_diagnostic_item(cx, callee_type, sym::Option)
58+
{
59+
span_lint(
60+
cx,
61+
SINGLE_OPTION_MAP,
62+
span,
63+
"mapping over single function argument of `Option<T>`, to return `Option<T>`, can be best expressed by applying the map outside of the function.",
64+
);
65+
}
66+
}
67+
}
68+
}

tests/ui/single_option_map.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![warn(clippy::single_option_map)]
2+
3+
fn h(arg: Option<u32>) -> Option<u32> {
4+
arg.map(|x| x * 2)
5+
}
6+
7+
fn j(arg: Option<u64>) -> Option<u64> {
8+
arg.map(|x| x * 2)
9+
}
10+
11+
fn main() {
12+
let answer = Some(42u32);
13+
let h_result = h(answer);
14+
15+
let answer = Some(42u64);
16+
let j_result = j(answer);
17+
}

tests/ui/single_option_map.stderr

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error: mapping over single function argument of `Option<T>`, to return `Option<T>`, can be best expressed by applying the map outside of the function.
2+
--> tests/ui/single_option_map.rs:3:1
3+
|
4+
LL | / fn h(arg: Option<u32>) -> Option<u32> {
5+
LL | | arg.map(|x| x * 2)
6+
LL | | }
7+
| |_^
8+
|
9+
= note: `-D clippy::single-option-map` implied by `-D warnings`
10+
= help: to override `-D warnings` add `#[allow(clippy::single_option_map)]`
11+
12+
error: mapping over single function argument of `Option<T>`, to return `Option<T>`, can be best expressed by applying the map outside of the function.
13+
--> tests/ui/single_option_map.rs:7:1
14+
|
15+
LL | / fn j(arg: Option<u64>) -> Option<u64> {
16+
LL | | arg.map(|x| x * 2)
17+
LL | | }
18+
| |_^
19+
20+
error: aborting due to 2 previous errors
21+

0 commit comments

Comments
 (0)