Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new lint unnecessary_wrap #6070

Merged
merged 29 commits into from
Nov 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a7ac441
Add new lint to detect unnecessarily wrapped value
hkmatsumoto Sep 19, 2020
750c118
Add suggestion on type signatures
hkmatsumoto Sep 20, 2020
0335b8d
Fix lint example
hkmatsumoto Sep 20, 2020
0e9d227
Add test cases
hkmatsumoto Sep 20, 2020
ebdd4e2
Refactor code according to reivews
hkmatsumoto Sep 22, 2020
6a62390
Optout rustfix test
hkmatsumoto Sep 22, 2020
3ed8902
Fix typo
hkmatsumoto Sep 22, 2020
c775856
Call `diag.multipart_suggestion` instead
hkmatsumoto Sep 22, 2020
a433d46
Run rustfmt
hkmatsumoto Sep 22, 2020
cdb72df
Split lint suggestion into two
hkmatsumoto Sep 26, 2020
6b55f3f
Add test case
hkmatsumoto Sep 26, 2020
df0d565
Move `find_all_ret_expressions` into `utils`
hkmatsumoto Oct 2, 2020
eec7f5c
Update clippy_lints/src/unnecessary_wrap.rs
hkmatsumoto Oct 12, 2020
1bdac87
Improve lint message
hkmatsumoto Oct 12, 2020
8392bc7
Run `cargo dev fmt`
hkmatsumoto Oct 12, 2020
2f85aa7
Make lint skip closures
hkmatsumoto Oct 18, 2020
12474c6
Add support for methods
hkmatsumoto Oct 18, 2020
c5447eb
Make lint skip macros
hkmatsumoto Oct 18, 2020
c7692cf
Skip function with no exprs contained
hkmatsumoto Oct 18, 2020
30632fb
Allow this lint on lint tests
hkmatsumoto Oct 18, 2020
e998d61
Downgrade applicability to MaybeIncorrect
hkmatsumoto Oct 18, 2020
9d96311
Remove wildcard use
hkmatsumoto Oct 18, 2020
532d205
Skip functions in PartialOrd
hkmatsumoto Oct 18, 2020
bf46f78
Fix clippy error
hkmatsumoto Oct 18, 2020
86331a4
Update stderr files
hkmatsumoto Nov 2, 2020
4c8d248
Update stderr files
hkmatsumoto Nov 14, 2020
4e5c02e
Ignore trait implementations
hkmatsumoto Nov 14, 2020
1f577c0
Fix embarrassing grammatical error
hkmatsumoto Nov 14, 2020
c7445d7
Pluralize lint name
hkmatsumoto Nov 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2008,6 +2008,7 @@ Released 2018-09-13
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
Expand Down
5 changes: 5 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ mod unicode;
mod unit_return_expecting_ord;
mod unnamed_address;
mod unnecessary_sort_by;
mod unnecessary_wraps;
mod unnested_or_patterns;
mod unsafe_removed_from_name;
mod unused_io_amount;
Expand Down Expand Up @@ -892,6 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&unnamed_address::FN_ADDRESS_COMPARISONS,
&unnamed_address::VTABLE_ADDRESS_COMPARISONS,
&unnecessary_sort_by::UNNECESSARY_SORT_BY,
&unnecessary_wraps::UNNECESSARY_WRAPS,
&unnested_or_patterns::UNNESTED_OR_PATTERNS,
&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
&unused_io_amount::UNUSED_IO_AMOUNT,
Expand Down Expand Up @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box redundant_clone::RedundantClone);
store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit);
store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy);
store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps);
store.register_late_pass(|| box types::RefToMut);
store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
Expand Down Expand Up @@ -1571,6 +1574,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(&unused_unit::UNUSED_UNIT),
Expand Down Expand Up @@ -1775,6 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::UNNECESSARY_CAST),
LintId::of(&types::VEC_BOX),
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&useless_conversion::USELESS_CONVERSION),
LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
Expand Down
125 changes: 1 addition & 124 deletions clippy_lints/src/methods/bind_instead_of_map.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use super::{contains_return, BIND_INSTEAD_OF_MAP};
use crate::utils::{
in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet,
snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then,
snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::Span;

pub(crate) struct OptionAndThenSome;
Expand Down Expand Up @@ -193,124 +191,3 @@ pub(crate) trait BindInsteadOfMap {
}
}
}

/// returns `true` if expr contains match expr desugared from try
fn contains_try(expr: &hir::Expr<'_>) -> bool {
struct TryFinder {
found: bool,
}

impl<'hir> intravisit::Visitor<'hir> for TryFinder {
type Map = Map<'hir>;

fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
intravisit::NestedVisitorMap::None
}

fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
if self.found {
return;
}
match expr.kind {
hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
_ => intravisit::walk_expr(self, expr),
}
}
}

let mut visitor = TryFinder { found: false };
visitor.visit_expr(expr);
visitor.found
}

fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
where
F: FnMut(&'hir hir::Expr<'hir>) -> bool,
{
struct RetFinder<F> {
in_stmt: bool,
failed: bool,
cb: F,
}

struct WithStmtGuarg<'a, F> {
val: &'a mut RetFinder<F>,
prev_in_stmt: bool,
}

impl<F> RetFinder<F> {
fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
WithStmtGuarg {
val: self,
prev_in_stmt,
}
}
}

impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
type Target = RetFinder<F>;

fn deref(&self) -> &Self::Target {
self.val
}
}

impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.val
}
}

impl<F> Drop for WithStmtGuarg<'_, F> {
fn drop(&mut self) {
self.val.in_stmt = self.prev_in_stmt;
}
}

impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
type Map = Map<'hir>;

fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
intravisit::NestedVisitorMap::None
}

fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
}

fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
if self.failed {
return;
}
if self.in_stmt {
match expr.kind {
hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
_ => intravisit::walk_expr(self, expr),
}
} else {
match expr.kind {
hir::ExprKind::Match(cond, arms, _) => {
self.inside_stmt(true).visit_expr(cond);
for arm in arms {
self.visit_expr(arm.body);
}
},
hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
_ => self.failed |= !(self.cb)(expr),
}
}
}
}

!contains_try(expr) && {
let mut ret_finder = RetFinder {
in_stmt: false,
failed: false,
cb: callback,
};
ret_finder.visit_expr(expr);
!ret_finder.failed
}
}
8 changes: 4 additions & 4 deletions clippy_lints/src/redundant_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,11 @@ fn find_stmt_assigns_to<'tcx>(

match (by_ref, &*rvalue) {
(true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
base_local_and_movability(cx, mir, *place)
Some(base_local_and_movability(cx, mir, *place))
},
(false, mir::Rvalue::Ref(_, _, place)) => {
if let [mir::ProjectionElem::Deref] = place.as_ref().projection {
base_local_and_movability(cx, mir, *place)
Some(base_local_and_movability(cx, mir, *place))
} else {
None
}
Expand All @@ -341,7 +341,7 @@ fn base_local_and_movability<'tcx>(
cx: &LateContext<'tcx>,
mir: &mir::Body<'tcx>,
place: mir::Place<'tcx>,
) -> Option<(mir::Local, CannotMoveOut)> {
) -> (mir::Local, CannotMoveOut) {
use rustc_middle::mir::PlaceRef;

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

Some((local, deref || field || slice))
(local, deref || field || slice)
}

struct LocalUseVisitor {
Expand Down
143 changes: 143 additions & 0 deletions clippy_lints/src/unnecessary_wraps.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use crate::utils::{
in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then,
visitors::find_all_ret_expressions,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;

declare_clippy_lint! {
/// **What it does:** Checks for private functions that only return `Ok` or `Some`.
///
/// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.
///
/// **Known problems:** Since this lint changes function type signature, you may need to
/// adjust some code at callee side.
///
/// **Example:**
///
/// ```rust
/// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
/// if a && b {
/// return Some(50);
/// }
/// if a {
/// Some(0)
/// } else {
/// Some(10)
/// }
/// }
/// ```
/// Use instead:
/// ```rust
/// fn get_cool_number(a: bool, b: bool) -> i32 {
/// if a && b {
/// return 50;
/// }
/// if a {
/// 0
/// } else {
/// 10
/// }
/// }
/// ```
pub UNNECESSARY_WRAPS,
complexity,
"functions that only return `Ok` or `Some`"
}

declare_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]);

impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
fn_kind: FnKind<'tcx>,
fn_decl: &FnDecl<'tcx>,
body: &Body<'tcx>,
span: Span,
hir_id: HirId,
) {
match fn_kind {
FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => {
if visibility.node.is_pub() {
return;
}
},
FnKind::Closure(..) => return,
_ => (),
}

if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) {
return;
}
}

let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) {
("Option", &paths::OPTION_SOME)
} else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
("Result", &paths::RESULT_OK)
} else {
return;
};

let mut suggs = Vec::new();
let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
if_chain! {
if !in_macro(ret_expr.span);
if let ExprKind::Call(ref func, ref args) = ret_expr.kind;
if let ExprKind::Path(ref qpath) = func.kind;
if match_qpath(qpath, path);
if args.len() == 1;
then {
suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string()));
true
} else {
false
}
}
});

if can_sugg && !suggs.is_empty() {
span_lint_and_then(
cx,
UNNECESSARY_WRAPS,
span,
format!(
"this function's return value is unnecessarily wrapped by `{}`",
return_type
)
.as_str(),
|diag| {
let inner_ty = return_ty(cx, hir_id)
.walk()
.skip(1) // skip `std::option::Option` or `std::result::Result`
.take(1) // take the first outermost inner type
.filter_map(|inner| match inner.unpack() {
GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()),
_ => None,
});
inner_ty.for_each(|inner_ty| {
diag.span_suggestion(
fn_decl.output.span(),
format!("remove `{}` from the return type...", return_type).as_str(),
inner_ty,
Applicability::MaybeIncorrect,
);
});
diag.multipart_suggestion(
"...and change the returning expressions",
suggs,
Applicability::MachineApplicable,
);
},
);
}
}
}
1 change: 1 addition & 0 deletions clippy_lints/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod ptr;
pub mod qualify_min_const_fn;
pub mod sugg;
pub mod usage;
pub mod visitors;

pub use self::attrs::*;
pub use self::diagnostics::*;
Expand Down
Loading