1 use clippy_utils
::diagnostics
::span_lint_and_help
;
2 use clippy_utils
::ty
::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}
;
3 use clippy_utils
::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}
;
4 use rustc_ast
::Mutability
;
5 use rustc_hir
::intravisit
::{walk_expr, Visitor}
;
6 use rustc_hir
::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind}
;
7 use rustc_lint
::{LateContext, LateLintPass}
;
8 use rustc_middle
::hir
::nested_filter
::OnlyBodies
;
9 use rustc_session
::declare_lint_pass
;
12 declare_clippy_lint
! {
14 /// Checks for the creation of a `peekable` iterator that is never `.peek()`ed
16 /// ### Why is this bad?
17 /// Creating a peekable iterator without using any of its methods is likely a mistake,
18 /// or just a leftover after a refactor.
22 /// let collection = vec![1, 2, 3];
23 /// let iter = collection.iter().peekable();
25 /// for item in iter {
32 /// let collection = vec![1, 2, 3];
33 /// let iter = collection.iter();
35 /// for item in iter {
39 #[clippy::version = "1.65.0"]
42 "creating a peekable iterator without using any of its methods"
45 declare_lint_pass
!(UnusedPeekable
=> [UNUSED_PEEKABLE
]);
47 impl<'tcx
> LateLintPass
<'tcx
> for UnusedPeekable
{
48 fn check_block(&mut self, cx
: &LateContext
<'tcx
>, block
: &Block
<'tcx
>) {
49 // Don't lint `Peekable`s returned from a block
50 if let Some(expr
) = block
.expr
51 && let Some(ty
) = cx
.typeck_results().expr_ty_opt(peel_ref_operators(cx
, expr
))
52 && is_type_diagnostic_item(cx
, ty
, sym
::IterPeekable
)
57 for (idx
, stmt
) in block
.stmts
.iter().enumerate() {
58 if !stmt
.span
.from_expansion()
59 && let StmtKind
::Local(local
) = stmt
.kind
60 && let PatKind
::Binding(_
, binding
, ident
, _
) = local
.pat
.kind
61 && let Some(init
) = local
.init
62 && !init
.span
.from_expansion()
63 && let Some(ty
) = cx
.typeck_results().expr_ty_opt(init
)
64 && let (ty
, _
, Mutability
::Mut
) = peel_mid_ty_refs_is_mutable(ty
)
65 && is_type_diagnostic_item(cx
, ty
, sym
::IterPeekable
)
67 let mut vis
= PeekableVisitor
::new(cx
, binding
);
69 if idx
+ 1 == block
.stmts
.len() && block
.expr
.is_none() {
73 for stmt
in &block
.stmts
[idx
..] {
77 if let Some(expr
) = block
.expr
{
81 if !vis
.found_peek_call
{
86 "`peek` never called on `Peekable` iterator",
88 "consider removing the call to `peekable`",
96 struct PeekableVisitor
<'a
, 'tcx
> {
97 cx
: &'a LateContext
<'tcx
>,
98 expected_hir_id
: HirId
,
99 found_peek_call
: bool
,
102 impl<'a
, 'tcx
> PeekableVisitor
<'a
, 'tcx
> {
103 fn new(cx
: &'a LateContext
<'tcx
>, expected_hir_id
: HirId
) -> Self {
107 found_peek_call
: false,
112 impl<'tcx
> Visitor
<'tcx
> for PeekableVisitor
<'_
, 'tcx
> {
113 type NestedFilter
= OnlyBodies
;
115 fn nested_visit_map(&mut self) -> Self::Map
{
119 fn visit_expr(&mut self, ex
: &'tcx Expr
<'tcx
>) {
120 if self.found_peek_call
{
124 if path_to_local_id(ex
, self.expected_hir_id
) {
125 for (_
, node
) in self.cx
.tcx
.hir().parent_iter(ex
.hir_id
) {
127 Node
::Expr(expr
) => {
129 // some_function(peekable)
131 // If the Peekable is passed to a function, stop
132 ExprKind
::Call(_
, args
) => {
133 if let Some(func_did
) = fn_def_id(self.cx
, expr
)
134 && let Some(into_iter_did
) = self.cx
.tcx
.lang_items().into_iter_fn()
135 && func_did
== into_iter_did
137 // Probably a for loop desugar, stop searching
141 if args
.iter().any(|arg
| arg_is_mut_peekable(self.cx
, arg
)) {
142 self.found_peek_call
= true;
147 // Catch anything taking a Peekable mutably
148 ExprKind
::MethodCall(
150 ident
: method_name_ident
,
157 let method_name
= method_name_ident
.name
.as_str();
159 // `Peekable` methods
160 if matches
!(method_name
, "peek" | "peek_mut" | "next_if" | "next_if_eq")
161 && arg_is_mut_peekable(self.cx
, self_arg
)
163 self.found_peek_call
= true;
167 // foo.some_method() excluding Iterator methods
168 if remaining_args
.iter().any(|arg
| arg_is_mut_peekable(self.cx
, arg
))
169 && !is_trait_method(self.cx
, expr
, sym
::Iterator
)
171 self.found_peek_call
= true;
175 // foo.by_ref(), keep checking for `peek`
176 if method_name
== "by_ref" {
182 ExprKind
::AddrOf(_
, Mutability
::Mut
, _
) | ExprKind
::Unary(..) | ExprKind
::DropTemps(_
) => {
184 ExprKind
::AddrOf(_
, Mutability
::Not
, _
) => return,
186 self.found_peek_call
= true;
191 Node
::Local(Local { init: Some(init), .. }
) => {
192 if arg_is_mut_peekable(self.cx
, init
) {
193 self.found_peek_call
= true;
198 Node
::Stmt(stmt
) => {
200 StmtKind
::Local(_
) | StmtKind
::Item(_
) => self.found_peek_call
= true,
201 StmtKind
::Expr(_
) | StmtKind
::Semi(_
) => {}
,
206 Node
::Block(_
) | Node
::ExprField(_
) => {}
,
218 fn arg_is_mut_peekable(cx
: &LateContext
<'_
>, arg
: &Expr
<'_
>) -> bool
{
219 if let Some(ty
) = cx
.typeck_results().expr_ty_opt(arg
)
220 && let (ty
, _
, Mutability
::Mut
) = peel_mid_ty_refs_is_mutable(ty
)
221 && is_type_diagnostic_item(cx
, ty
, sym
::IterPeekable
)