Skip to content

Commit cb05701

Browse files
committed
Auto merge of #11413 - jonboh:master, r=Alexendoo
new unnecessary_map_on_constructor lint changelog: [`unnecessary_map_on_constructor`]: adds lint for cases in which map is not necessary. `Some(4).map(myfunction)` => `Some(myfunction(4))` Closes #6472 Note that the case mentioned in the issue `Some(..).and_then(|..| Some(..))` is fixed by a chain of lint changes. This PR completes the last part of that chain. By `bind_instead_of_map`[lint](https://rust-lang.github.io/rust-clippy/master/index.html#/bind_instead_of_map): `Some(4).and_then(|x| Some(foo(4)))` => `Some(4).map(|x| foo)` By `redundant_closure` [lint](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure): `Some(4).map(|x| foo)` => `Some(4).map(fun)` Finally by this PR `unnecessary_map_on_constructor`: `Some(4).map(fun)` => `Some(fun(4))` I'm not sure this is the desired behavior for clippy and if it should be addressed in another issue/PR. I'd be up to give it a try if that's the case.
2 parents 98363cb + f136e16 commit cb05701

16 files changed

+319
-53
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5437,6 +5437,7 @@ Released 2018-09-13
54375437
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
54385438
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
54395439
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
5440+
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
54405441
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
54415442
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
54425443
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
671671
crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
672672
crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
673673
crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
674+
crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
674675
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
675676
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
676677
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ mod unit_return_expecting_ord;
329329
mod unit_types;
330330
mod unnamed_address;
331331
mod unnecessary_box_returns;
332+
mod unnecessary_map_on_constructor;
332333
mod unnecessary_owned_empty_strings;
333334
mod unnecessary_self_imports;
334335
mod unnecessary_struct_initialization;
@@ -1102,6 +1103,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11021103
store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
11031104
store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
11041105
store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
1106+
store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor));
11051107
// add lints here, do not remove this comment, it's used in `new_lint`
11061108
}
11071109

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

tests/ui/eta.fixed

+2-1
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

+2-1
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

+27-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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`
@@ -8,31 +8,31 @@ LL | let a = Some(1u8).map(|a| foo(a));
88
= help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]`
99

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

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

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

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

3434
error: redundant closure
35-
--> $DIR/eta.rs:93:51
35+
--> $DIR/eta.rs:94:51
3636
|
3737
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
3838
| ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
@@ -41,127 +41,127 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
4141
= help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]`
4242

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

tests/ui/manual_map_option.fixed

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

+1
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)