@@ -68,7 +68,44 @@ declare_clippy_lint! {
6868 "traits or impls with a public `len` method but no corresponding `is_empty` method"
6969}
7070
71- declare_lint_pass ! ( LenZero => [ LEN_ZERO , LEN_WITHOUT_IS_EMPTY ] ) ;
71+ declare_clippy_lint ! {
72+ /// **What it does:** Checks for comparing to an empty slice such as "" or [],`
73+ /// and suggests using `.is_empty()` where applicable.
74+ ///
75+ /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
76+ /// than checking for equality. So it is good to get into the habit of using
77+ /// `.is_empty()`, and having it is cheap.
78+ /// Besides, it makes the intent clearer than a manual comparison in some contexts.
79+ ///
80+ /// **Known problems:** None.
81+ ///
82+ /// **Example:**
83+ ///
84+ /// ```ignore
85+ /// if s == "" {
86+ /// ..
87+ /// }
88+ ///
89+ /// if arr == [] {
90+ /// ..
91+ /// }
92+ /// ```
93+ /// Use instead:
94+ /// ```ignore
95+ /// if s.is_empty() {
96+ /// ..
97+ /// }
98+ ///
99+ /// if arr.is_empty() {
100+ /// ..
101+ /// }
102+ /// ```
103+ pub COMPARISON_TO_EMPTY ,
104+ style,
105+ "checking `x == \" \" ` or `x == []` (or similar) when `.is_empty()` could be used instead"
106+ }
107+
108+ declare_lint_pass ! ( LenZero => [ LEN_ZERO , LEN_WITHOUT_IS_EMPTY , COMPARISON_TO_EMPTY ] ) ;
72109
73110impl < ' tcx > LateLintPass < ' tcx > for LenZero {
74111 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
@@ -221,6 +258,8 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
221258 }
222259
223260 check_len ( cx, span, method_path. ident . name , args, & lit. node , op, compare_to)
261+ } else {
262+ check_empty_expr ( cx, span, method, lit, op)
224263 }
225264}
226265
@@ -258,6 +297,42 @@ fn check_len(
258297 }
259298}
260299
300+ fn check_empty_expr ( cx : & LateContext < ' _ > , span : Span , lit1 : & Expr < ' _ > , lit2 : & Expr < ' _ > , op : & str ) {
301+ if ( is_empty_array ( lit2) || is_empty_string ( lit2) ) && has_is_empty ( cx, lit1) {
302+ let mut applicability = Applicability :: MachineApplicable ;
303+ span_lint_and_sugg (
304+ cx,
305+ COMPARISON_TO_EMPTY ,
306+ span,
307+ "comparison to empty slice" ,
308+ & format ! ( "using `{}is_empty` is clearer and more explicit" , op) ,
309+ format ! (
310+ "{}{}.is_empty()" ,
311+ op,
312+ snippet_with_applicability( cx, lit1. span, "_" , & mut applicability)
313+ ) ,
314+ applicability,
315+ ) ;
316+ }
317+ }
318+
319+ fn is_empty_string ( expr : & Expr < ' _ > ) -> bool {
320+ if let ExprKind :: Lit ( ref lit) = expr. kind {
321+ if let LitKind :: Str ( lit, _) = lit. node {
322+ let lit = lit. as_str ( ) ;
323+ return lit == "" ;
324+ }
325+ }
326+ false
327+ }
328+
329+ fn is_empty_array ( expr : & Expr < ' _ > ) -> bool {
330+ if let ExprKind :: Array ( ref arr) = expr. kind {
331+ return arr. is_empty ( ) ;
332+ }
333+ false
334+ }
335+
261336/// Checks if this type has an `is_empty` method.
262337fn has_is_empty ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
263338 /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
0 commit comments