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

New lint for all/any after mapping to bool #8396

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -3270,6 +3270,7 @@ Released 2018-09-13
[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
[`map_then_identity_transformer`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_then_identity_transformer
[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
Expand Down
6 changes: 4 additions & 2 deletions clippy_lints/src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,10 @@ fn check_unsafe_derive_deserialize<'tcx>(
if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
if cx.tcx.inherent_impls(def.did)
.iter()
.map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
.any(|imp| has_unsafe(cx, imp));
.any(|imp_did| {
let imp = cx.tcx.hir().expect_item(imp_did.expect_local());
has_unsafe(cx, imp)
});
then {
span_lint_and_help(
cx,
Expand Down
3 changes: 1 addition & 2 deletions clippy_lints/src/disallowed_script_idents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ impl DisallowedScriptIdents {
pub fn new(whitelist: &[String]) -> Self {
let whitelist = whitelist
.iter()
.map(String::as_str)
.filter_map(Script::from_full_name)
.filter_map(|s| Script::from_full_name(String::as_str(s)))
.collect();
Self { whitelist }
}
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/lib.register_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ store.register_lints(&[
methods::MAP_COLLECT_RESULT_UNIT,
methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_THEN_IDENTITY_TRANSFORMER,
methods::MAP_UNWRAP_OR,
methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF,
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/lib.register_pedantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MAP_THEN_IDENTITY_TRANSFORMER),
LintId::of(methods::MAP_UNWRAP_OR),
LintId::of(misc::FLOAT_CMP),
LintId::of(misc::USED_UNDERSCORE_BINDING),
Expand Down
15 changes: 9 additions & 6 deletions clippy_lints/src/loops/never_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
}

fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
es.map(|e| never_loop_expr(e, main_loop_id))
.fold(NeverLoopResult::Otherwise, combine_seq)
es.fold(NeverLoopResult::Otherwise, |seq, e| {
combine_seq(seq, never_loop_expr(e, main_loop_id))
})
}

fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
Expand Down Expand Up @@ -189,13 +190,15 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
}

fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
es.map(|e| never_loop_expr(e, main_loop_id))
.fold(NeverLoopResult::Otherwise, combine_both)
es.fold(NeverLoopResult::Otherwise, |seq, e| {
combine_both(seq, never_loop_expr(e, main_loop_id))
})
}

fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult {
e.map(|e| never_loop_expr(e, main_loop_id))
.fold(NeverLoopResult::AlwaysBreak, combine_branches)
e.fold(NeverLoopResult::AlwaysBreak, |br, e| {
combine_branches(br, never_loop_expr(e, main_loop_id))
})
}

fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {
Expand Down
3 changes: 1 addition & 2 deletions clippy_lints/src/matches/redundant_pattern_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, see
ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen),
ty::Adt(adt, subs) => adt
.all_fields()
.map(|f| f.ty(cx.tcx, subs))
.any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
.any(|f| type_needs_ordered_drop_inner(cx, f.ty(cx.tcx, subs), seen)),
_ => true,
}
}
Expand Down
11 changes: 10 additions & 1 deletion clippy_lints/src/methods/filter_map_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ use rustc_span::{source_map::Span, sym};

use super::FILTER_MAP_IDENTITY;

pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
filter_map_arg: &hir::Expr<'_>,
filter_map_span: Span,
) -> bool {
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
span_lint_and_sugg(
cx,
Expand All @@ -18,5 +23,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg:
"flatten()".to_string(),
Applicability::MachineApplicable,
);
// Returns a boolean indicating whether this lint has been triggered or not
true
} else {
false
}
}
6 changes: 5 additions & 1 deletion clippy_lints/src/methods/flat_map_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(
expr: &'tcx hir::Expr<'_>,
flat_map_arg: &'tcx hir::Expr<'_>,
flat_map_span: Span,
) {
) -> bool {
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
span_lint_and_sugg(
cx,
Expand All @@ -24,5 +24,9 @@ pub(super) fn check<'tcx>(
"flatten()".to_string(),
Applicability::MachineApplicable,
);
// Returns a boolean indicating whether this lint has been triggered or not
true
} else {
false
}
}
15 changes: 11 additions & 4 deletions clippy_lints/src/methods/flat_map_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,23 @@ use rustc_span::{source_map::Span, sym};
use super::FLAT_MAP_OPTION;
use clippy_utils::ty::is_type_diagnostic_item;

pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
arg: &'tcx hir::Expr<'_>,
span: Span,
) -> bool {
if !is_trait_method(cx, expr, sym::Iterator) {
return;
return false;
}
let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
let sig = match arg_ty.kind() {
ty::Closure(_, substs) => substs.as_closure().sig(),
_ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx),
_ => return,
_ => return false,
};
if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::Option) {
return;
return false;
}
span_lint_and_sugg(
cx,
Expand All @@ -31,4 +36,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg
"filter_map".into(),
Applicability::MachineApplicable,
);
// Returns a boolean indicating whether this lint has been triggered or not
true
}
136 changes: 136 additions & 0 deletions clippy_lints/src/methods/map_then_identity_transformer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::path_to_local_id;
use clippy_utils::visitors::expr_visitor;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{BodyId, Expr, ExprKind, PatKind};
use rustc_lint::{LateContext, LintContext};
use rustc_span::{MultiSpan, Span};

use super::MAP_THEN_IDENTITY_TRANSFORMER;

pub(super) fn check<'tcx>(
cx: &LateContext<'_>,
map_span: Span,
map_name: &str,
map_param: &'tcx Expr<'_>,
transformer_name: &str,
// Use the last parameter of the transformer because the transfomer may be `fold(_, _)`
transformer_last_param: &'tcx Expr<'_>,
) {
match &transformer_last_param.kind {
ExprKind::Closure(_, _, transformer_clos_body_id, _, _) => {
match &map_param.kind {
// map(Closure).<transformer>(Closure)
ExprKind::Closure(_, _, map_clos_body_id, _, _) => {
let map_clos_val = &cx.tcx.hir().body(*map_clos_body_id).value;
if_chain!(
// checks if the body of the closure of the `map` is an one-line expression
if is_one_line(cx, map_clos_val.span);
// checks if the parameter of the closure of the transformer appears once in its body
if let Some(refd_param_span) = refd_param_span(cx, *transformer_clos_body_id);
then {
span_lint_and_then(
cx,
MAP_THEN_IDENTITY_TRANSFORMER,
MultiSpan::from_span(map_span),
&format!("this `{map_name}` can be collapsed into the `{transformer_name}`"),
|diag| {
let mut help_span = MultiSpan::from_spans(vec![map_clos_val.span, refd_param_span]);
help_span.push_span_label(refd_param_span, "replace this variable".into());
help_span.push_span_label(map_clos_val.span, "with this expression".into());
diag.span_help(help_span, &format!("these `{map_name}` and `{transformer_name}` can be merged into a single `{transformer_name}`"));
},
);
}

);
},
// map(Path).<transformer>(Closure)
ExprKind::Path(_) => {
if_chain!(
// checks if the parameter of the `map` fits within one line
if is_one_line(cx, map_param.span);
// checks if the parameter of the closure of the transformer appears once in its body
if let Some(refd_param_span) = refd_param_span(cx, *transformer_clos_body_id);
then {
span_lint_and_then(
cx,
MAP_THEN_IDENTITY_TRANSFORMER,
MultiSpan::from_span(map_span),
&format!("this `{map_name}` can be collapsed into the `{transformer_name}`"),
|diag| {
let mut help_span = MultiSpan::from_spans(vec![map_param.span, refd_param_span]);
help_span.push_span_label(map_param.span, "apply this".into());
help_span.push_span_label(refd_param_span, "to this variable".into());
diag.span_help(help_span, &format!("these `{map_name}` and `{transformer_name}` can be merged into a single `{transformer_name}`"));
},
);
}

);
},
_ => (),
}
},
// map(Path).<transformer>(Path) or map(Closure).<transformer>(Path)
ExprKind::Path(_) => {
// checks if the parameter of the `map` fits within one line
if is_one_line(cx, map_param.span) {
span_lint_and_then(
cx,
MAP_THEN_IDENTITY_TRANSFORMER,
MultiSpan::from_span(map_span),
&format!("this `{map_name}` can be collapsed into the `{transformer_name}`"),
|diag| {
let mut help_span = MultiSpan::from_spans(vec![map_param.span, transformer_last_param.span]);
help_span.push_span_label(map_param.span, format!("and use this in the `{transformer_name}`"));
help_span.push_span_label(transformer_last_param.span, "change this to a closure".into());
diag.span_help(help_span, &format!("these `{map_name}` and `{transformer_name}` can be merged into a single `{transformer_name}`"));
},
);
}
},
_ => (),
}
}

// Given a closure `|.., x| y`, checks if `x` is referenced just exactly once in `y` and returns
// the span of `x` in `y`
fn refd_param_span(cx: &LateContext<'_>, clos_body_id: BodyId) -> Option<Span> {
let clos_body = cx.tcx.hir().body(clos_body_id);
if_chain! {
if let [.., clos_param] = clos_body.params;
if let PatKind::Binding(_, clos_param_id, _, _) = clos_param.pat.kind;
then {
let clos_val = &clos_body.value;
let mut count = 0;
let mut span = None;
expr_visitor(cx, |expr| {
if path_to_local_id(expr, clos_param_id) {
count += 1;
span = Some(expr.span);
}
count <= 1
})
.visit_expr(clos_val);

if count == 1 {
span
} else {
None
}
} else {
None
}
}
}

fn is_one_line(cx: &LateContext<'_>, span: Span) -> bool {
let src = cx.sess().source_map();
if let Ok(lo) = src.lookup_line(span.lo()) {
if let Ok(hi) = src.lookup_line(span.hi()) {
return hi.line == lo.line;
}
}
false
}
Loading