Skip to content

Commit 7b62d1f

Browse files
committed
Better handle method/function calls
1 parent 5986708 commit 7b62d1f

File tree

4 files changed

+86
-39
lines changed

4 files changed

+86
-39
lines changed

clippy_lints/src/lib.register_suspicious.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
3232
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
3333
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
3434
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
35-
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
3635
LintId::of(unused_peekable::UNUSED_PEEKABLE),
36+
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
3737
])

clippy_lints/src/unused_peekable.rs

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
22
use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable};
3-
use clippy_utils::{fn_def_id, path_to_local_id, paths, peel_ref_operators};
3+
use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators};
44
use rustc_ast::Mutability;
55
use rustc_hir::intravisit::{walk_expr, Visitor};
66
use rustc_hir::lang_items::LangItem;
77
use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind};
88
use rustc_lint::{LateContext, LateLintPass};
9-
use rustc_middle::ty::Ty;
109
use rustc_session::{declare_tool_lint, impl_lint_pass};
10+
use rustc_span::sym;
1111

1212
declare_clippy_lint! {
1313
/// ### What it does
@@ -70,7 +70,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
7070
&& let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
7171
&& match_type(cx, ty, &paths::PEEKABLE)
7272
{
73-
let mut vis = PeekableVisitor::new(cx, local.pat.hir_id);
73+
self.visited.push(local.pat.hir_id);
74+
75+
let mut vis = PeekableVisitor::new(cx, local.pat.hir_id, &mut self.visited);
7476

7577
if idx + 1 == block.stmts.len() && block.expr.is_none() {
7678
return;
@@ -103,20 +105,26 @@ struct PeekableVisitor<'a, 'tcx> {
103105
cx: &'a LateContext<'tcx>,
104106
expected_hir_id: HirId,
105107
found_peek_call: bool,
108+
visited: &'a mut Vec<HirId>,
106109
}
107110

108111
impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
109-
fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self {
112+
fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId, visited: &'a mut Vec<HirId>) -> Self {
110113
Self {
111114
cx,
112115
expected_hir_id,
113116
found_peek_call: false,
117+
visited,
114118
}
115119
}
116120
}
117121

118122
impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
119123
fn visit_expr(&mut self, ex: &'_ Expr<'_>) {
124+
if self.found_peek_call {
125+
return;
126+
}
127+
120128
if path_to_local_id(ex, self.expected_hir_id) {
121129
for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) {
122130
match node {
@@ -138,54 +146,57 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
138146
return;
139147
}
140148

141-
for arg in args.iter().map(|arg| peel_ref_operators(self.cx, arg)) {
142-
if let ExprKind::Path(_) = arg.kind
143-
&& let Some(ty) = self
144-
.cx
145-
.typeck_results()
146-
.expr_ty_opt(arg)
147-
.map(Ty::peel_refs)
148-
&& match_type(self.cx, ty, &paths::PEEKABLE)
149-
{
150-
self.found_peek_call = true;
151-
return;
152-
}
149+
if args.iter().any(|arg| {
150+
matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg)
151+
}) {
152+
self.found_peek_call = true;
153+
return;
153154
}
154155
},
155-
// Peekable::peek()
156-
ExprKind::MethodCall(PathSegment { ident: method_name, .. }, [arg, ..], _) => {
157-
let arg = peel_ref_operators(self.cx, arg);
158-
let method_name = method_name.name.as_str();
159-
160-
if (method_name == "peek"
161-
|| method_name == "peek_mut"
162-
|| method_name == "next_if"
163-
|| method_name == "next_if_eq")
164-
&& let ExprKind::Path(_) = arg.kind
165-
&& let Some(ty) = self.cx.typeck_results().expr_ty_opt(arg).map(Ty::peel_refs)
166-
&& match_type(self.cx, ty, &paths::PEEKABLE)
156+
// Catch anything taking a Peekable mutably
157+
ExprKind::MethodCall(
158+
PathSegment { ident: method_name, .. },
159+
[self_arg, remaining_args @ ..],
160+
_,
161+
) => {
162+
// `Peekable` methods
163+
if matches!(
164+
method_name.name.as_str(),
165+
"peek" | "peek_mut" | "next_if" | "next_if_eq"
166+
) && arg_is_mut_peekable(self.cx, self_arg)
167167
{
168168
self.found_peek_call = true;
169169
return;
170170
}
171+
172+
// foo.some_method() excluding Iterator methods
173+
if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg))
174+
&& !is_trait_method(self.cx, expr, sym::Iterator)
175+
{
176+
self.found_peek_call = true;
177+
return;
178+
}
179+
180+
return;
171181
},
172-
// Don't bother if moved into a struct
173-
ExprKind::Struct(..) => {
182+
ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => {
183+
},
184+
ExprKind::AddrOf(_, Mutability::Not, _) => return,
185+
_ => {
174186
self.found_peek_call = true;
175187
return;
176188
},
177-
_ => {},
178189
}
179190
},
180-
Node::Local(Local { init: Some(init), .. }) => {
181-
if let Some(ty) = self.cx.typeck_results().expr_ty_opt(init)
182-
&& let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
183-
&& match_type(self.cx, ty, &paths::PEEKABLE)
184-
{
191+
Node::Local(Local {
192+
init: Some(init), pat, ..
193+
}) => {
194+
if arg_is_mut_peekable(self.cx, init) {
185195
self.found_peek_call = true;
186196
return;
187197
}
188198

199+
self.visited.push(pat.hir_id);
189200
break;
190201
},
191202
Node::Stmt(stmt) => match stmt.kind {
@@ -206,3 +217,14 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
206217
walk_expr(self, ex);
207218
}
208219
}
220+
221+
fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
222+
if let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
223+
&& let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
224+
&& match_type(cx, ty, &paths::PEEKABLE)
225+
{
226+
true
227+
} else {
228+
false
229+
}
230+
}

tests/ui/unused_peekable.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ fn invalid() {
3232
let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable();
3333
peekable_using_iterator_method.next();
3434

35+
// Passed by ref to another function
36+
fn takes_ref(_peek: &Peekable<Empty<u32>>) {}
37+
let passed_along_ref = std::iter::empty::<u32>().peekable();
38+
takes_ref(&passed_along_ref);
39+
3540
let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable();
3641
for x in peekable_in_for_loop {}
3742
}
@@ -43,6 +48,18 @@ fn valid() {
4348
let passed_along = std::iter::empty::<u32>().peekable();
4449
takes_peekable(passed_along);
4550

51+
// Passed to another method
52+
struct PeekableConsumer;
53+
impl PeekableConsumer {
54+
fn consume(&self, _: Peekable<Empty<u32>>) {}
55+
fn consume_mut_ref(&self, _: &mut Peekable<Empty<u32>>) {}
56+
}
57+
58+
let peekable_consumer = PeekableConsumer;
59+
let mut passed_along_to_method = std::iter::empty::<u32>().peekable();
60+
peekable_consumer.consume_mut_ref(&mut passed_along_to_method);
61+
peekable_consumer.consume(passed_along_to_method);
62+
4663
// `peek` called in another block
4764
let mut peekable_in_block = std::iter::empty::<u32>().peekable();
4865
{

tests/ui/unused_peekable.stderr

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,20 @@ LL | let mut peekable_using_iterator_method = std::iter::empty::<u32>().peek
4040
= help: consider removing the call to `peekable`
4141

4242
error: `peek` never called on `Peekable` iterator
43-
--> $DIR/unused_peekable.rs:35:13
43+
--> $DIR/unused_peekable.rs:37:9
44+
|
45+
LL | let passed_along_ref = std::iter::empty::<u32>().peekable();
46+
| ^^^^^^^^^^^^^^^^
47+
|
48+
= help: consider removing the call to `peekable`
49+
50+
error: `peek` never called on `Peekable` iterator
51+
--> $DIR/unused_peekable.rs:40:13
4452
|
4553
LL | let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable();
4654
| ^^^^^^^^^^^^^^^^^^^^
4755
|
4856
= help: consider removing the call to `peekable`
4957

50-
error: aborting due to 6 previous errors
58+
error: aborting due to 7 previous errors
5159

0 commit comments

Comments
 (0)