Skip to content

Commit eb65729

Browse files
committed
prereqs
1 parent ca06aa2 commit eb65729

13 files changed

+227
-295
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6304,6 +6304,7 @@ Released 2018-09-13
63046304
[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
63056305
[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
63066306
[`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards
6307+
[`redundant_iter_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_iter_cloned
63076308
[`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals
63086309
[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
63096310
[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
449449
crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
450450
crate::methods::READONLY_WRITE_LOCK_INFO,
451451
crate::methods::REDUNDANT_AS_STR_INFO,
452+
crate::methods::REDUNDANT_ITER_CLONED_INFO,
452453
crate::methods::REPEAT_ONCE_INFO,
453454
crate::methods::RESULT_FILTER_MAP_INFO,
454455
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
use crate::methods::method_call;
21
use clippy_utils::diagnostics::span_lint_and_sugg;
3-
use clippy_utils::{peel_blocks, sym};
2+
use clippy_utils::source::SpanRangeExt;
3+
use clippy_utils::ty::is_type_diagnostic_item;
4+
use clippy_utils::{peel_blocks, peel_hir_expr_while, sym};
45
use rustc_ast::LitKind;
56
use rustc_errors::Applicability;
67
use rustc_hir::{Expr, ExprKind};
78
use rustc_lint::{LateContext, LateLintPass};
8-
use rustc_middle::ty;
99
use rustc_session::declare_lint_pass;
10-
use rustc_span::{BytePos, Span};
1110

1211
declare_clippy_lint! {
1312
/// ### What it does
@@ -43,53 +42,58 @@ declare_clippy_lint! {
4342

4443
declare_lint_pass!(IneffectiveOpenOptions => [INEFFECTIVE_OPEN_OPTIONS]);
4544

46-
fn index_if_arg_is_boolean(args: &[Expr<'_>], call_span: Span) -> Option<Span> {
47-
if let [arg] = args
48-
&& let ExprKind::Lit(lit) = peel_blocks(arg).kind
49-
&& lit.node == LitKind::Bool(true)
50-
{
51-
// The `.` is not included in the span so we cheat a little bit to include it as well.
52-
Some(call_span.with_lo(call_span.lo() - BytePos(1)))
53-
} else {
54-
None
55-
}
56-
}
57-
5845
impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions {
5946
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
60-
let Some((sym::open, mut receiver, [_arg], _, _)) = method_call(expr) else {
61-
return;
62-
};
63-
let receiver_ty = cx.typeck_results().expr_ty(receiver);
64-
match receiver_ty.peel_refs().kind() {
65-
ty::Adt(adt, _) if cx.tcx.is_diagnostic_item(sym::FsOpenOptions, adt.did()) => {},
66-
_ => return,
67-
}
68-
69-
let mut append = None;
70-
let mut write = None;
47+
if let ExprKind::MethodCall(name, recv, [_], _) = expr.kind
48+
&& name.ident.name == sym::open
49+
&& !expr.span.from_expansion()
50+
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::FsOpenOptions)
51+
{
52+
let mut append = false;
53+
let mut write = None;
54+
peel_hir_expr_while(recv, |e| {
55+
if let ExprKind::MethodCall(name, recv, args, call_span) = e.kind
56+
&& !e.span.from_expansion()
57+
{
58+
if let [arg] = args
59+
&& let ExprKind::Lit(lit) = peel_blocks(arg).kind
60+
&& matches!(lit.node, LitKind::Bool(true))
61+
&& !arg.span.from_expansion()
62+
&& !lit.span.from_expansion()
63+
{
64+
match name.ident.name {
65+
sym::append => append = true,
66+
sym::write
67+
if let Some(range) = call_span.map_range(cx, |_, text, range| {
68+
if text.get(..range.start)?.ends_with('.') {
69+
Some(range.start - 1..range.end)
70+
} else {
71+
None
72+
}
73+
}) =>
74+
{
75+
write = Some(call_span.with_lo(range.start));
76+
},
77+
_ => {},
78+
}
79+
}
80+
Some(recv)
81+
} else {
82+
None
83+
}
84+
});
7185

72-
while let Some((name, recv, args, _, span)) = method_call(receiver) {
73-
if name == sym::append {
74-
append = index_if_arg_is_boolean(args, span);
75-
} else if name == sym::write {
76-
write = index_if_arg_is_boolean(args, span);
86+
if append && let Some(write_span) = write {
87+
span_lint_and_sugg(
88+
cx,
89+
INEFFECTIVE_OPEN_OPTIONS,
90+
write_span,
91+
"unnecessary use of `.write(true)` because there is `.append(true)`",
92+
"remove `.write(true)`",
93+
String::new(),
94+
Applicability::MachineApplicable,
95+
);
7796
}
78-
receiver = recv;
79-
}
80-
81-
if let Some(write_span) = write
82-
&& append.is_some()
83-
{
84-
span_lint_and_sugg(
85-
cx,
86-
INEFFECTIVE_OPEN_OPTIONS,
87-
write_span,
88-
"unnecessary use of `.write(true)` because there is `.append(true)`",
89-
"remove `.write(true)`",
90-
String::new(),
91-
Applicability::MachineApplicable,
92-
);
9397
}
9498
}
9599
}

clippy_lints/src/loops/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
876876
missing_spin_loop::check(cx, condition, body);
877877
manual_while_let_some::check(cx, condition, body, span);
878878
}
879+
880+
if let ExprKind::MethodCall(path, recv, [arg], _) = expr.kind
881+
&& matches!(
882+
path.ident.name,
883+
sym::all | sym::any | sym::filter_map | sym::find_map | sym::flat_map | sym::for_each | sym::map
884+
)
885+
{
886+
unused_enumerate_index::check_method(cx, expr, recv, arg);
887+
}
879888
}
880889
}
881890

@@ -904,7 +913,7 @@ impl Loops {
904913
same_item_push::check(cx, pat, arg, body, expr, self.msrv);
905914
manual_flatten::check(cx, pat, arg, body, span, self.msrv);
906915
manual_find::check(cx, pat, arg, body, span, expr);
907-
unused_enumerate_index::check(cx, pat, arg, body);
916+
unused_enumerate_index::check_loop(cx, arg, pat, None, body);
908917
char_indices_as_byte_indices::check(cx, pat, arg, body);
909918
}
910919

clippy_lints/src/loops/unused_enumerate_index.rs

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,84 @@
11
use super::UNUSED_ENUMERATE_INDEX;
22
use clippy_utils::diagnostics::span_lint_and_then;
3-
use clippy_utils::source::snippet;
4-
use clippy_utils::{pat_is_wild, sugg};
3+
use clippy_utils::source::{SpanRangeExt, walk_span_to_context};
4+
use clippy_utils::ty::is_type_diagnostic_item;
5+
use clippy_utils::{expr_or_init, is_lint_allowed, is_trait_method, pat_is_wild};
56
use rustc_errors::Applicability;
6-
use rustc_hir::def::DefKind;
7-
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
7+
use rustc_hir::{Expr, ExprKind, Pat, PatKind, TyKind};
88
use rustc_lint::LateContext;
9-
use rustc_middle::ty;
10-
use rustc_span::sym;
9+
use rustc_span::{Span, SyntaxContext, sym};
1110

12-
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
13-
///
14-
/// The lint is also partially implemented in `clippy_lints/src/methods/unused_enumerate_index.rs`.
15-
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) {
16-
if let PatKind::Tuple([index, elem], _) = pat.kind
17-
&& let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind
18-
&& let ty = cx.typeck_results().expr_ty(arg)
19-
&& pat_is_wild(cx, &index.kind, body)
20-
&& let ty::Adt(base, _) = *ty.kind()
21-
&& cx.tcx.is_diagnostic_item(sym::Enumerate, base.did())
22-
&& let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
23-
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id)
11+
pub(super) fn check_method<'tcx>(
12+
cx: &LateContext<'tcx>,
13+
e: &'tcx Expr<'tcx>,
14+
recv: &'tcx Expr<'tcx>,
15+
arg: &'tcx Expr<'tcx>,
16+
) {
17+
if let ExprKind::Closure(closure) = arg.kind
18+
&& let body = cx.tcx.hir_body(closure.body)
19+
&& let [param] = body.params
20+
&& is_trait_method(cx, e, sym::Iterator)
21+
&& let [input] = closure.fn_decl.inputs
22+
&& !arg.span.from_expansion()
23+
&& !input.span.from_expansion()
24+
&& !recv.span.from_expansion()
25+
&& !param.span.from_expansion()
2426
{
27+
let ty_spans = if let TyKind::Tup([_, inner]) = input.kind {
28+
let Some(inner) = walk_span_to_context(inner.span, SyntaxContext::root()) else {
29+
return;
30+
};
31+
Some((input.span, inner))
32+
} else {
33+
None
34+
};
35+
check_loop(cx, recv, param.pat, ty_spans, body.value);
36+
}
37+
}
38+
39+
pub(super) fn check_loop<'tcx>(
40+
cx: &LateContext<'tcx>,
41+
e: &'tcx Expr<'tcx>,
42+
pat: &'tcx Pat<'tcx>,
43+
ty_spans: Option<(Span, Span)>,
44+
body: &'tcx Expr<'tcx>,
45+
) {
46+
if let PatKind::Tuple([idx_pat, inner_pat], _) = pat.kind
47+
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e), sym::Enumerate)
48+
&& pat_is_wild(cx, &idx_pat.kind, body)
49+
&& let enumerate_call = expr_or_init(cx, e)
50+
&& let ExprKind::MethodCall(_, _, [], enumerate_span) = enumerate_call.kind
51+
&& let Some(enumerate_id) = cx.typeck_results().type_dependent_def_id(enumerate_call.hir_id)
52+
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_id)
53+
&& !is_lint_allowed(cx, UNUSED_ENUMERATE_INDEX, enumerate_call.hir_id)
54+
&& !enumerate_call.span.from_expansion()
55+
&& !pat.span.from_expansion()
56+
&& !idx_pat.span.from_expansion()
57+
&& !inner_pat.span.from_expansion()
58+
&& let Some(enumerate_range) = enumerate_span.map_range(cx, |_, text, range| {
59+
text.get(..range.start)?
60+
.ends_with('.')
61+
.then_some(range.start - 1..range.end)
62+
})
63+
{
64+
let enumerate_span = Span::new(enumerate_range.start, enumerate_range.end, SyntaxContext::root(), None);
2565
span_lint_and_then(
2666
cx,
2767
UNUSED_ENUMERATE_INDEX,
28-
arg.span,
68+
enumerate_span,
2969
"you seem to use `.enumerate()` and immediately discard the index",
3070
|diag| {
31-
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
71+
let mut spans = Vec::with_capacity(5);
72+
spans.push((enumerate_span, String::new()));
73+
spans.push((pat.span.with_hi(inner_pat.span.lo()), String::new()));
74+
spans.push((pat.span.with_lo(inner_pat.span.hi()), String::new()));
75+
if let Some((outer, inner)) = ty_spans {
76+
spans.push((outer.with_hi(inner.lo()), String::new()));
77+
spans.push((outer.with_lo(inner.hi()), String::new()));
78+
}
3279
diag.multipart_suggestion(
3380
"remove the `.enumerate()` call",
34-
vec![
35-
(pat.span, snippet(cx, elem.span, "..").into_owned()),
36-
(arg.span, base_iter.to_string()),
37-
],
81+
spans,
3882
Applicability::MachineApplicable,
3983
);
4084
},

clippy_lints/src/methods/iter_overeager_cloned.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability};
1010
use rustc_middle::ty::{self, BorrowKind};
1111
use rustc_span::{Symbol, sym};
1212

13-
use super::ITER_OVEREAGER_CLONED;
14-
use crate::redundant_clone::REDUNDANT_CLONE;
13+
use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED};
1514

1615
#[derive(Clone, Copy)]
1716
pub(super) enum Op<'a> {
@@ -96,7 +95,7 @@ pub(super) fn check<'tcx>(
9695
}
9796

9897
let (lint, msg, trailing_clone) = match op {
99-
Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""),
98+
Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_ITER_CLONED, "unneeded cloning of iterator items", ""),
10099
Op::LaterCloned | Op::FixClosure(_, _) => (
101100
ITER_OVEREAGER_CLONED,
102101
"unnecessarily eager cloning of iterator items",

0 commit comments

Comments
 (0)