Skip to content

Commit 6c116ed

Browse files
committed
Auto merge of #6070 - matsujika:unnecessary_wrap, r=flip1995
Add new lint `unnecessary_wrap` Fixes #5969 changelog: New lint [`unnecessary_wraps`]
2 parents ad4f829 + c7445d7 commit 6c116ed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+754
-347
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2008,6 +2008,7 @@ Released 2018-09-13
20082008
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
20092009
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
20102010
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
2011+
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
20112012
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
20122013
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
20132014
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns

clippy_lints/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ mod unicode;
323323
mod unit_return_expecting_ord;
324324
mod unnamed_address;
325325
mod unnecessary_sort_by;
326+
mod unnecessary_wraps;
326327
mod unnested_or_patterns;
327328
mod unsafe_removed_from_name;
328329
mod unused_io_amount;
@@ -892,6 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
892893
&unnamed_address::FN_ADDRESS_COMPARISONS,
893894
&unnamed_address::VTABLE_ADDRESS_COMPARISONS,
894895
&unnecessary_sort_by::UNNECESSARY_SORT_BY,
896+
&unnecessary_wraps::UNNECESSARY_WRAPS,
895897
&unnested_or_patterns::UNNESTED_OR_PATTERNS,
896898
&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
897899
&unused_io_amount::UNUSED_IO_AMOUNT,
@@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10641066
store.register_late_pass(|| box redundant_clone::RedundantClone);
10651067
store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit);
10661068
store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy);
1069+
store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps);
10671070
store.register_late_pass(|| box types::RefToMut);
10681071
store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
10691072
store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
@@ -1571,6 +1574,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
15711574
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
15721575
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
15731576
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
1577+
LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
15741578
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
15751579
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
15761580
LintId::of(&unused_unit::UNUSED_UNIT),
@@ -1775,6 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
17751779
LintId::of(&types::UNNECESSARY_CAST),
17761780
LintId::of(&types::VEC_BOX),
17771781
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
1782+
LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
17781783
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
17791784
LintId::of(&useless_conversion::USELESS_CONVERSION),
17801785
LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),

clippy_lints/src/methods/bind_instead_of_map.rs

+1-124
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use super::{contains_return, BIND_INSTEAD_OF_MAP};
22
use crate::utils::{
33
in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet,
4-
snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then,
4+
snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions,
55
};
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
88
use rustc_hir as hir;
9-
use rustc_hir::intravisit::{self, Visitor};
109
use rustc_lint::LateContext;
11-
use rustc_middle::hir::map::Map;
1210
use rustc_span::Span;
1311

1412
pub(crate) struct OptionAndThenSome;
@@ -193,124 +191,3 @@ pub(crate) trait BindInsteadOfMap {
193191
}
194192
}
195193
}
196-
197-
/// returns `true` if expr contains match expr desugared from try
198-
fn contains_try(expr: &hir::Expr<'_>) -> bool {
199-
struct TryFinder {
200-
found: bool,
201-
}
202-
203-
impl<'hir> intravisit::Visitor<'hir> for TryFinder {
204-
type Map = Map<'hir>;
205-
206-
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
207-
intravisit::NestedVisitorMap::None
208-
}
209-
210-
fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
211-
if self.found {
212-
return;
213-
}
214-
match expr.kind {
215-
hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
216-
_ => intravisit::walk_expr(self, expr),
217-
}
218-
}
219-
}
220-
221-
let mut visitor = TryFinder { found: false };
222-
visitor.visit_expr(expr);
223-
visitor.found
224-
}
225-
226-
fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
227-
where
228-
F: FnMut(&'hir hir::Expr<'hir>) -> bool,
229-
{
230-
struct RetFinder<F> {
231-
in_stmt: bool,
232-
failed: bool,
233-
cb: F,
234-
}
235-
236-
struct WithStmtGuarg<'a, F> {
237-
val: &'a mut RetFinder<F>,
238-
prev_in_stmt: bool,
239-
}
240-
241-
impl<F> RetFinder<F> {
242-
fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
243-
let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
244-
WithStmtGuarg {
245-
val: self,
246-
prev_in_stmt,
247-
}
248-
}
249-
}
250-
251-
impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
252-
type Target = RetFinder<F>;
253-
254-
fn deref(&self) -> &Self::Target {
255-
self.val
256-
}
257-
}
258-
259-
impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
260-
fn deref_mut(&mut self) -> &mut Self::Target {
261-
self.val
262-
}
263-
}
264-
265-
impl<F> Drop for WithStmtGuarg<'_, F> {
266-
fn drop(&mut self) {
267-
self.val.in_stmt = self.prev_in_stmt;
268-
}
269-
}
270-
271-
impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
272-
type Map = Map<'hir>;
273-
274-
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
275-
intravisit::NestedVisitorMap::None
276-
}
277-
278-
fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
279-
intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
280-
}
281-
282-
fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
283-
if self.failed {
284-
return;
285-
}
286-
if self.in_stmt {
287-
match expr.kind {
288-
hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
289-
_ => intravisit::walk_expr(self, expr),
290-
}
291-
} else {
292-
match expr.kind {
293-
hir::ExprKind::Match(cond, arms, _) => {
294-
self.inside_stmt(true).visit_expr(cond);
295-
for arm in arms {
296-
self.visit_expr(arm.body);
297-
}
298-
},
299-
hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
300-
hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
301-
_ => self.failed |= !(self.cb)(expr),
302-
}
303-
}
304-
}
305-
}
306-
307-
!contains_try(expr) && {
308-
let mut ret_finder = RetFinder {
309-
in_stmt: false,
310-
failed: false,
311-
cb: callback,
312-
};
313-
ret_finder.visit_expr(expr);
314-
!ret_finder.failed
315-
}
316-
}

clippy_lints/src/redundant_clone.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,11 @@ fn find_stmt_assigns_to<'tcx>(
320320

321321
match (by_ref, &*rvalue) {
322322
(true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
323-
base_local_and_movability(cx, mir, *place)
323+
Some(base_local_and_movability(cx, mir, *place))
324324
},
325325
(false, mir::Rvalue::Ref(_, _, place)) => {
326326
if let [mir::ProjectionElem::Deref] = place.as_ref().projection {
327-
base_local_and_movability(cx, mir, *place)
327+
Some(base_local_and_movability(cx, mir, *place))
328328
} else {
329329
None
330330
}
@@ -341,7 +341,7 @@ fn base_local_and_movability<'tcx>(
341341
cx: &LateContext<'tcx>,
342342
mir: &mir::Body<'tcx>,
343343
place: mir::Place<'tcx>,
344-
) -> Option<(mir::Local, CannotMoveOut)> {
344+
) -> (mir::Local, CannotMoveOut) {
345345
use rustc_middle::mir::PlaceRef;
346346

347347
// Dereference. You cannot move things out from a borrowed value.
@@ -362,7 +362,7 @@ fn base_local_and_movability<'tcx>(
362362
&& !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
363363
}
364364

365-
Some((local, deref || field || slice))
365+
(local, deref || field || slice)
366366
}
367367

368368
struct LocalUseVisitor {

clippy_lints/src/unnecessary_wraps.rs

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use crate::utils::{
2+
in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then,
3+
visitors::find_all_ret_expressions,
4+
};
5+
use if_chain::if_chain;
6+
use rustc_errors::Applicability;
7+
use rustc_hir::intravisit::FnKind;
8+
use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node};
9+
use rustc_lint::{LateContext, LateLintPass};
10+
use rustc_middle::ty::subst::GenericArgKind;
11+
use rustc_session::{declare_lint_pass, declare_tool_lint};
12+
use rustc_span::Span;
13+
14+
declare_clippy_lint! {
15+
/// **What it does:** Checks for private functions that only return `Ok` or `Some`.
16+
///
17+
/// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.
18+
///
19+
/// **Known problems:** Since this lint changes function type signature, you may need to
20+
/// adjust some code at callee side.
21+
///
22+
/// **Example:**
23+
///
24+
/// ```rust
25+
/// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
26+
/// if a && b {
27+
/// return Some(50);
28+
/// }
29+
/// if a {
30+
/// Some(0)
31+
/// } else {
32+
/// Some(10)
33+
/// }
34+
/// }
35+
/// ```
36+
/// Use instead:
37+
/// ```rust
38+
/// fn get_cool_number(a: bool, b: bool) -> i32 {
39+
/// if a && b {
40+
/// return 50;
41+
/// }
42+
/// if a {
43+
/// 0
44+
/// } else {
45+
/// 10
46+
/// }
47+
/// }
48+
/// ```
49+
pub UNNECESSARY_WRAPS,
50+
complexity,
51+
"functions that only return `Ok` or `Some`"
52+
}
53+
54+
declare_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]);
55+
56+
impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
57+
fn check_fn(
58+
&mut self,
59+
cx: &LateContext<'tcx>,
60+
fn_kind: FnKind<'tcx>,
61+
fn_decl: &FnDecl<'tcx>,
62+
body: &Body<'tcx>,
63+
span: Span,
64+
hir_id: HirId,
65+
) {
66+
match fn_kind {
67+
FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => {
68+
if visibility.node.is_pub() {
69+
return;
70+
}
71+
},
72+
FnKind::Closure(..) => return,
73+
_ => (),
74+
}
75+
76+
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
77+
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) {
78+
return;
79+
}
80+
}
81+
82+
let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) {
83+
("Option", &paths::OPTION_SOME)
84+
} else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
85+
("Result", &paths::RESULT_OK)
86+
} else {
87+
return;
88+
};
89+
90+
let mut suggs = Vec::new();
91+
let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
92+
if_chain! {
93+
if !in_macro(ret_expr.span);
94+
if let ExprKind::Call(ref func, ref args) = ret_expr.kind;
95+
if let ExprKind::Path(ref qpath) = func.kind;
96+
if match_qpath(qpath, path);
97+
if args.len() == 1;
98+
then {
99+
suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string()));
100+
true
101+
} else {
102+
false
103+
}
104+
}
105+
});
106+
107+
if can_sugg && !suggs.is_empty() {
108+
span_lint_and_then(
109+
cx,
110+
UNNECESSARY_WRAPS,
111+
span,
112+
format!(
113+
"this function's return value is unnecessarily wrapped by `{}`",
114+
return_type
115+
)
116+
.as_str(),
117+
|diag| {
118+
let inner_ty = return_ty(cx, hir_id)
119+
.walk()
120+
.skip(1) // skip `std::option::Option` or `std::result::Result`
121+
.take(1) // take the first outermost inner type
122+
.filter_map(|inner| match inner.unpack() {
123+
GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()),
124+
_ => None,
125+
});
126+
inner_ty.for_each(|inner_ty| {
127+
diag.span_suggestion(
128+
fn_decl.output.span(),
129+
format!("remove `{}` from the return type...", return_type).as_str(),
130+
inner_ty,
131+
Applicability::MaybeIncorrect,
132+
);
133+
});
134+
diag.multipart_suggestion(
135+
"...and change the returning expressions",
136+
suggs,
137+
Applicability::MachineApplicable,
138+
);
139+
},
140+
);
141+
}
142+
}
143+
}

clippy_lints/src/utils/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod ptr;
2121
pub mod qualify_min_const_fn;
2222
pub mod sugg;
2323
pub mod usage;
24+
pub mod visitors;
2425

2526
pub use self::attrs::*;
2627
pub use self::diagnostics::*;

0 commit comments

Comments
 (0)