1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::source
::snippet_with_applicability
;
3 use clippy_utils
::ty
::match_type
;
4 use clippy_utils
::{contains_name, get_pat_name, paths, single_segment_path}
;
5 use if_chain
::if_chain
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
9 use rustc_middle
::ty
::{self, UintTy}
;
10 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
12 use rustc_span
::Symbol
;
14 declare_clippy_lint
! {
15 /// **What it does:** Checks for naive byte counts
17 /// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)
18 /// crate has methods to count your bytes faster, especially for large slices.
20 /// **Known problems:** If you have predominantly small slices, the
21 /// `bytecount::count(..)` method may actually be slower. However, if you can
22 /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
23 /// faster in those cases.
28 /// # let vec = vec![1_u8];
29 /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
33 "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
36 declare_lint_pass
!(ByteCount
=> [NAIVE_BYTECOUNT
]);
38 impl<'tcx
> LateLintPass
<'tcx
> for ByteCount
{
39 fn check_expr(&mut self, cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) {
41 if let ExprKind
::MethodCall(count
, _
, count_args
, _
) = expr
.kind
;
42 if count
.ident
.name
== sym
!(count
);
43 if count_args
.len() == 1;
44 if let ExprKind
::MethodCall(filter
, _
, filter_args
, _
) = count_args
[0].kind
;
45 if filter
.ident
.name
== sym
!(filter
);
46 if filter_args
.len() == 2;
47 if let ExprKind
::Closure(_
, _
, body_id
, _
, _
) = filter_args
[1].kind
;
48 let body
= cx
.tcx
.hir().body(body_id
);
49 if body
.params
.len() == 1;
50 if let Some(argname
) = get_pat_name(body
.params
[0].pat
);
51 if let ExprKind
::Binary(ref op
, l
, r
) = body
.value
.kind
;
52 if op
.node
== BinOpKind
::Eq
;
54 cx
.typeck_results().expr_ty(&filter_args
[0]).peel_refs(),
57 let needle
= match get_path_name(l
) {
58 Some(name
) if check_arg(name
, argname
, r
) => r
,
59 _
=> match get_path_name(r
) {
60 Some(name
) if check_arg(name
, argname
, l
) => l
,
64 if ty
::Uint(UintTy
::U8
) != *cx
.typeck_results().expr_ty(needle
).peel_refs().kind() {
67 let haystack
= if let ExprKind
::MethodCall(path
, _
, args
, _
) =
69 let p
= path
.ident
.name
;
70 if (p
== sym
::iter
|| p
== sym
!(iter_mut
)) && args
.len() == 1 {
78 let mut applicability
= Applicability
::MaybeIncorrect
;
83 "you appear to be counting bytes the naive way",
84 "consider using the bytecount crate",
85 format
!("bytecount::count({}, {})",
86 snippet_with_applicability(cx
, haystack
.span
, "..", &mut applicability
),
87 snippet_with_applicability(cx
, needle
.span
, "..", &mut applicability
)),
95 fn check_arg(name
: Symbol
, arg
: Symbol
, needle
: &Expr
<'_
>) -> bool
{
96 name
== arg
&& !contains_name(name
, needle
)
99 fn get_path_name(expr
: &Expr
<'_
>) -> Option
<Symbol
> {
101 ExprKind
::Box(e
) | ExprKind
::AddrOf(BorrowKind
::Ref
, _
, e
) | ExprKind
::Unary(UnOp
::Deref
, e
) => {
104 ExprKind
::Block(b
, _
) => {
105 if b
.stmts
.is_empty() {
106 b
.expr
.as_ref().and_then(|p
| get_path_name(p
))
111 ExprKind
::Path(ref qpath
) => single_segment_path(qpath
).map(|ps
| ps
.ident
.name
),