Skip to content

Commit 5418ef0

Browse files
committed
new unnecessary_map_on_constructor lint
1 parent 8c20739 commit 5418ef0

16 files changed

+295
-53
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5433,6 +5433,7 @@ Released 2018-09-13
54335433
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
54345434
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
54355435
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
5436+
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
54365437
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
54375438
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
54385439
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
669669
crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
670670
crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
671671
crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
672+
crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
672673
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
673674
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
674675
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ mod unit_return_expecting_ord;
328328
mod unit_types;
329329
mod unnamed_address;
330330
mod unnecessary_box_returns;
331+
mod unnecessary_map_on_constructor;
331332
mod unnecessary_owned_empty_strings;
332333
mod unnecessary_self_imports;
333334
mod unnecessary_struct_initialization;
@@ -1099,6 +1100,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10991100
store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
11001101
store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
11011102
store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
1103+
store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor));
11021104
// add lints here, do not remove this comment, it's used in `new_lint`
11031105
}
11041106

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_applicability;
3+
use clippy_utils::ty::is_type_diagnostic_item;
4+
use rustc_errors::Applicability;
5+
use rustc_hir as hir;
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
use rustc_span::sym;
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// Suggest removing the use of a may (or map_err) method when an Option or Result is being construted.
13+
/// ### Why is this bad?
14+
/// It introduces unnecessary complexity. In this case the function can be used directly and
15+
/// construct the Option or Result from the output.
16+
///
17+
/// ### Example
18+
/// ```rust
19+
/// Some(4).map(foo)
20+
/// ```
21+
/// Use instead:
22+
/// ```rust
23+
/// Some(foo(4))
24+
/// ```
25+
#[clippy::version = "1.73.0"]
26+
pub UNNECESSARY_MAP_ON_CONSTRUCTOR,
27+
complexity,
28+
"Suggest removing usage of map in Option or Result construction"
29+
}
30+
declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTOR]);
31+
32+
impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
33+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
34+
if expr.span.from_expansion() {
35+
return;
36+
}
37+
if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind {
38+
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
39+
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
40+
41+
if let (false, false) = (is_option, is_result) {
42+
return;
43+
};
44+
45+
let (constructor_path, constructor_item) = if_chain! {
46+
if let hir::ExprKind::Call(constructor, constructor_args) = recv.kind;
47+
if let hir::ExprKind::Path(constructor_path) = constructor.kind;
48+
if let Some(arg) = constructor_args.get(0);
49+
then {
50+
(constructor_path, arg)
51+
} else {
52+
return;
53+
}
54+
};
55+
let constructor_symbol = match constructor_path {
56+
hir::QPath::Resolved(_, path) => {
57+
if let Some(path_segment) = path.segments.last() {
58+
path_segment.ident.name
59+
} else {
60+
return;
61+
}
62+
},
63+
hir::QPath::TypeRelative(_, path) => path.ident.name,
64+
hir::QPath::LangItem(_, _, _) => return,
65+
};
66+
match constructor_symbol {
67+
sym::Some | sym::Ok if path.ident.name == rustc_span::sym::map => (),
68+
sym::Err if path.ident.name == sym!(map_err) => (),
69+
_ => return,
70+
}
71+
72+
if_chain! {
73+
if let Some(arg) = args.get(0);
74+
if let hir::ExprKind::Path(fun) = arg.kind;
75+
then {
76+
let mut applicability = Applicability::MachineApplicable;
77+
let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability);
78+
let constructor_snippet =
79+
snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability);
80+
let constructor_arg_snippet =
81+
snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability);
82+
let suggestion = format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))");
83+
span_lint_and_sugg(
84+
cx,
85+
UNNECESSARY_MAP_ON_CONSTRUCTOR,
86+
expr.span,
87+
&format!("unnecessary {fun_snippet} on contstuctor {constructor_snippet}(_)"),
88+
"try using",
89+
suggestion,
90+
applicability,
91+
);
92+
}
93+
}
94+
}
95+
}
96+
}

tests/ui/eta.fixed

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
clippy::option_map_unit_fn,
88
clippy::redundant_closure_call,
99
clippy::uninlined_format_args,
10-
clippy::useless_vec
10+
clippy::useless_vec,
11+
clippy::unnecessary_map_on_constructor
1112
)]
1213

1314
use std::path::{Path, PathBuf};

tests/ui/eta.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
clippy::option_map_unit_fn,
88
clippy::redundant_closure_call,
99
clippy::uninlined_format_args,
10-
clippy::useless_vec
10+
clippy::useless_vec,
11+
clippy::unnecessary_map_on_constructor
1112
)]
1213

1314
use std::path::{Path, PathBuf};

tests/ui/eta.stderr

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,165 @@
11
error: redundant closure
2-
--> $DIR/eta.rs:28:27
2+
--> $DIR/eta.rs:29:27
33
|
44
LL | let a = Some(1u8).map(|a| foo(a));
55
| ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
66
|
77
= note: `-D clippy::redundant-closure` implied by `-D warnings`
88

99
error: redundant closure
10-
--> $DIR/eta.rs:32:40
10+
--> $DIR/eta.rs:33:40
1111
|
1212
LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
1313
| ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
1414

1515
error: redundant closure
16-
--> $DIR/eta.rs:33:35
16+
--> $DIR/eta.rs:34:35
1717
|
1818
LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
1919
| ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
2020

2121
error: redundant closure
22-
--> $DIR/eta.rs:34:26
22+
--> $DIR/eta.rs:35:26
2323
|
2424
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
2525
| ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
2626

2727
error: redundant closure
28-
--> $DIR/eta.rs:41:27
28+
--> $DIR/eta.rs:42:27
2929
|
3030
LL | let e = Some(1u8).map(|a| generic(a));
3131
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
3232

3333
error: redundant closure
34-
--> $DIR/eta.rs:93:51
34+
--> $DIR/eta.rs:94:51
3535
|
3636
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
3737
| ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
3838
|
3939
= note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
4040

4141
error: redundant closure
42-
--> $DIR/eta.rs:94:51
42+
--> $DIR/eta.rs:95:51
4343
|
4444
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
4545
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
4646

4747
error: redundant closure
48-
--> $DIR/eta.rs:96:42
48+
--> $DIR/eta.rs:97:42
4949
|
5050
LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
5151
| ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
5252

5353
error: redundant closure
54-
--> $DIR/eta.rs:100:29
54+
--> $DIR/eta.rs:101:29
5555
|
5656
LL | let e = Some("str").map(|s| s.to_string());
5757
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
5858

5959
error: redundant closure
60-
--> $DIR/eta.rs:101:27
60+
--> $DIR/eta.rs:102:27
6161
|
6262
LL | let e = Some('a').map(|s| s.to_uppercase());
6363
| ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
6464

6565
error: redundant closure
66-
--> $DIR/eta.rs:103:65
66+
--> $DIR/eta.rs:104:65
6767
|
6868
LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
6969
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
7070

7171
error: redundant closure
72-
--> $DIR/eta.rs:166:22
72+
--> $DIR/eta.rs:167:22
7373
|
7474
LL | requires_fn_once(|| x());
7575
| ^^^^^^ help: replace the closure with the function itself: `x`
7676

7777
error: redundant closure
78-
--> $DIR/eta.rs:173:27
78+
--> $DIR/eta.rs:174:27
7979
|
8080
LL | let a = Some(1u8).map(|a| foo_ptr(a));
8181
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
8282

8383
error: redundant closure
84-
--> $DIR/eta.rs:178:27
84+
--> $DIR/eta.rs:179:27
8585
|
8686
LL | let a = Some(1u8).map(|a| closure(a));
8787
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
8888

8989
error: redundant closure
90-
--> $DIR/eta.rs:210:28
90+
--> $DIR/eta.rs:211:28
9191
|
9292
LL | x.into_iter().for_each(|x| add_to_res(x));
9393
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
9494

9595
error: redundant closure
96-
--> $DIR/eta.rs:211:28
96+
--> $DIR/eta.rs:212:28
9797
|
9898
LL | y.into_iter().for_each(|x| add_to_res(x));
9999
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
100100

101101
error: redundant closure
102-
--> $DIR/eta.rs:212:28
102+
--> $DIR/eta.rs:213:28
103103
|
104104
LL | z.into_iter().for_each(|x| add_to_res(x));
105105
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
106106

107107
error: redundant closure
108-
--> $DIR/eta.rs:219:21
108+
--> $DIR/eta.rs:220:21
109109
|
110110
LL | Some(1).map(|n| closure(n));
111111
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
112112

113113
error: redundant closure
114-
--> $DIR/eta.rs:223:21
114+
--> $DIR/eta.rs:224:21
115115
|
116116
LL | Some(1).map(|n| in_loop(n));
117117
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
118118

119119
error: redundant closure
120-
--> $DIR/eta.rs:316:18
120+
--> $DIR/eta.rs:317:18
121121
|
122122
LL | takes_fn_mut(|| f());
123123
| ^^^^^^ help: replace the closure with the function itself: `&mut f`
124124

125125
error: redundant closure
126-
--> $DIR/eta.rs:319:19
126+
--> $DIR/eta.rs:320:19
127127
|
128128
LL | takes_fn_once(|| f());
129129
| ^^^^^^ help: replace the closure with the function itself: `&mut f`
130130

131131
error: redundant closure
132-
--> $DIR/eta.rs:323:26
132+
--> $DIR/eta.rs:324:26
133133
|
134134
LL | move || takes_fn_mut(|| f_used_once())
135135
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
136136

137137
error: redundant closure
138-
--> $DIR/eta.rs:335:19
138+
--> $DIR/eta.rs:336:19
139139
|
140140
LL | array_opt.map(|a| a.as_slice());
141141
| ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice`
142142

143143
error: redundant closure
144-
--> $DIR/eta.rs:338:19
144+
--> $DIR/eta.rs:339:19
145145
|
146146
LL | slice_opt.map(|s| s.len());
147147
| ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len`
148148

149149
error: redundant closure
150-
--> $DIR/eta.rs:341:17
150+
--> $DIR/eta.rs:342:17
151151
|
152152
LL | ptr_opt.map(|p| p.is_null());
153153
| ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null`
154154

155155
error: redundant closure
156-
--> $DIR/eta.rs:345:17
156+
--> $DIR/eta.rs:346:17
157157
|
158158
LL | dyn_opt.map(|d| d.method_on_dyn());
159159
| ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
160160

161161
error: redundant closure
162-
--> $DIR/eta.rs:388:19
162+
--> $DIR/eta.rs:389:19
163163
|
164164
LL | let _ = f(&0, |x, y| f2(x, y));
165165
| ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2`

tests/ui/manual_map_option.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
clippy::unit_arg,
66
clippy::match_ref_pats,
77
clippy::redundant_pattern_matching,
8+
clippy::unnecessary_map_on_constructor,
89
for_loops_over_fallibles,
910
dead_code
1011
)]

tests/ui/manual_map_option.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
clippy::unit_arg,
66
clippy::match_ref_pats,
77
clippy::redundant_pattern_matching,
8+
clippy::unnecessary_map_on_constructor,
89
for_loops_over_fallibles,
910
dead_code
1011
)]

0 commit comments

Comments
 (0)