From 7a0f0c0e8b79d0dd95e0c97660e327e51fa09c0f Mon Sep 17 00:00:00 2001
From: mdgaziur <mdgaziurrahmannoor@gmail.com>
Date: Sat, 26 Nov 2022 12:17:49 +0600
Subject: [PATCH] Fix #9958

---
 clippy_lints/src/len_zero.rs | 20 +++++++---
 tests/ui/len_zero.fixed      | 40 +++++++++++++++++++
 tests/ui/len_zero.rs         | 40 +++++++++++++++++++
 tests/ui/len_zero.stderr     | 74 ++++++++++++++++++++++++++++--------
 4 files changed, 153 insertions(+), 21 deletions(-)

diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs
index 4c133c06a15..3babc2a5895 100644
--- a/clippy_lints/src/len_zero.rs
+++ b/clippy_lints/src/len_zero.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed};
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{
     def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
-    ItemKind, Mutability, Node, TraitItemRef, TyKind,
+    ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@@ -16,6 +16,7 @@ use rustc_span::{
     source_map::{Span, Spanned, Symbol},
     symbol::sym,
 };
+use std::borrow::Cow;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -428,16 +429,23 @@ fn check_len(
 fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
     if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
         let mut applicability = Applicability::MachineApplicable;
+
+        let lit1 = peel_ref_operators(cx, lit1);
+        let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability);
+
+        // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will
+        // cause the code to dereference boolean(won't compile).
+        if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind {
+            lit_str = Cow::from(format!("({lit_str})"));
+        }
+
         span_lint_and_sugg(
             cx,
             COMPARISON_TO_EMPTY,
             span,
             "comparison to empty slice",
             &format!("using `{op}is_empty` is clearer and more explicit"),
-            format!(
-                "{op}{}.is_empty()",
-                snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
-            ),
+            format!("{op}{lit_str}.is_empty()"),
             applicability,
         );
     }
diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed
index 1f3b8ac99b1..c1c0b5ae40f 100644
--- a/tests/ui/len_zero.fixed
+++ b/tests/ui/len_zero.fixed
@@ -3,6 +3,9 @@
 #![warn(clippy::len_zero)]
 #![allow(dead_code, unused, clippy::len_without_is_empty)]
 
+extern crate core;
+use core::ops::Deref;
+
 pub struct One;
 struct Wither;
 
@@ -56,6 +59,26 @@ impl WithIsEmpty for Wither {
     }
 }
 
+struct DerefToDerefToString;
+
+impl Deref for DerefToDerefToString {
+    type Target = DerefToString;
+
+    fn deref(&self) -> &Self::Target {
+        &DerefToString {}
+    }
+}
+
+struct DerefToString;
+
+impl Deref for DerefToString {
+    type Target = str;
+
+    fn deref(&self) -> &Self::Target {
+        "Hello, world!"
+    }
+}
+
 fn main() {
     let x = [1, 2];
     if x.is_empty() {
@@ -64,6 +87,23 @@ fn main() {
 
     if "".is_empty() {}
 
+    let s = "Hello, world!";
+    let s1 = &s;
+    let s2 = &s1;
+    let s3 = &s2;
+    let s4 = &s3;
+    let s5 = &s4;
+    let s6 = &s5;
+    println!("{}", s1.is_empty());
+    println!("{}", s2.is_empty());
+    println!("{}", s3.is_empty());
+    println!("{}", s4.is_empty());
+    println!("{}", s5.is_empty());
+    println!("{}", (s6).is_empty());
+
+    let d2s = DerefToDerefToString {};
+    println!("{}", (**d2s).is_empty());
+
     let y = One;
     if y.len() == 0 {
         // No error; `One` does not have `.is_empty()`.
diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs
index dc21de0001b..cc2eb05b6bf 100644
--- a/tests/ui/len_zero.rs
+++ b/tests/ui/len_zero.rs
@@ -3,6 +3,9 @@
 #![warn(clippy::len_zero)]
 #![allow(dead_code, unused, clippy::len_without_is_empty)]
 
+extern crate core;
+use core::ops::Deref;
+
 pub struct One;
 struct Wither;
 
@@ -56,6 +59,26 @@ impl WithIsEmpty for Wither {
     }
 }
 
+struct DerefToDerefToString;
+
+impl Deref for DerefToDerefToString {
+    type Target = DerefToString;
+
+    fn deref(&self) -> &Self::Target {
+        &DerefToString {}
+    }
+}
+
+struct DerefToString;
+
+impl Deref for DerefToString {
+    type Target = str;
+
+    fn deref(&self) -> &Self::Target {
+        "Hello, world!"
+    }
+}
+
 fn main() {
     let x = [1, 2];
     if x.len() == 0 {
@@ -64,6 +87,23 @@ fn main() {
 
     if "".len() == 0 {}
 
+    let s = "Hello, world!";
+    let s1 = &s;
+    let s2 = &s1;
+    let s3 = &s2;
+    let s4 = &s3;
+    let s5 = &s4;
+    let s6 = &s5;
+    println!("{}", *s1 == "");
+    println!("{}", **s2 == "");
+    println!("{}", ***s3 == "");
+    println!("{}", ****s4 == "");
+    println!("{}", *****s5 == "");
+    println!("{}", ******(s6) == "");
+
+    let d2s = DerefToDerefToString {};
+    println!("{}", &**d2s == "");
+
     let y = One;
     if y.len() == 0 {
         // No error; `One` does not have `.is_empty()`.
diff --git a/tests/ui/len_zero.stderr b/tests/ui/len_zero.stderr
index 6c71f1beeac..b6f13780253 100644
--- a/tests/ui/len_zero.stderr
+++ b/tests/ui/len_zero.stderr
@@ -1,5 +1,5 @@
 error: length comparison to zero
-  --> $DIR/len_zero.rs:61:8
+  --> $DIR/len_zero.rs:84:8
    |
 LL |     if x.len() == 0 {
    |        ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()`
@@ -7,82 +7,126 @@ LL |     if x.len() == 0 {
    = note: `-D clippy::len-zero` implied by `-D warnings`
 
 error: length comparison to zero
-  --> $DIR/len_zero.rs:65:8
+  --> $DIR/len_zero.rs:88:8
    |
 LL |     if "".len() == 0 {}
    |        ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()`
 
+error: comparison to empty slice
+  --> $DIR/len_zero.rs:97:20
+   |
+LL |     println!("{}", *s1 == "");
+   |                    ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s1.is_empty()`
+   |
+   = note: `-D clippy::comparison-to-empty` implied by `-D warnings`
+
+error: comparison to empty slice
+  --> $DIR/len_zero.rs:98:20
+   |
+LL |     println!("{}", **s2 == "");
+   |                    ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s2.is_empty()`
+
+error: comparison to empty slice
+  --> $DIR/len_zero.rs:99:20
+   |
+LL |     println!("{}", ***s3 == "");
+   |                    ^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s3.is_empty()`
+
+error: comparison to empty slice
+  --> $DIR/len_zero.rs:100:20
+   |
+LL |     println!("{}", ****s4 == "");
+   |                    ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s4.is_empty()`
+
+error: comparison to empty slice
+  --> $DIR/len_zero.rs:101:20
+   |
+LL |     println!("{}", *****s5 == "");
+   |                    ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s5.is_empty()`
+
+error: comparison to empty slice
+  --> $DIR/len_zero.rs:102:20
+   |
+LL |     println!("{}", ******(s6) == "");
+   |                    ^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(s6).is_empty()`
+
+error: comparison to empty slice
+  --> $DIR/len_zero.rs:105:20
+   |
+LL |     println!("{}", &**d2s == "");
+   |                    ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()`
+
 error: length comparison to zero
-  --> $DIR/len_zero.rs:80:8
+  --> $DIR/len_zero.rs:120:8
    |
 LL |     if has_is_empty.len() == 0 {
    |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
 
 error: length comparison to zero
-  --> $DIR/len_zero.rs:83:8
+  --> $DIR/len_zero.rs:123:8
    |
 LL |     if has_is_empty.len() != 0 {
    |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
 
 error: length comparison to zero
-  --> $DIR/len_zero.rs:86:8
+  --> $DIR/len_zero.rs:126:8
    |
 LL |     if has_is_empty.len() > 0 {
    |        ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
 
 error: length comparison to one
-  --> $DIR/len_zero.rs:89:8
+  --> $DIR/len_zero.rs:129:8
    |
 LL |     if has_is_empty.len() < 1 {
    |        ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
 
 error: length comparison to one
-  --> $DIR/len_zero.rs:92:8
+  --> $DIR/len_zero.rs:132:8
    |
 LL |     if has_is_empty.len() >= 1 {
    |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
 
 error: length comparison to zero
-  --> $DIR/len_zero.rs:103:8
+  --> $DIR/len_zero.rs:143:8
    |
 LL |     if 0 == has_is_empty.len() {
    |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
 
 error: length comparison to zero
-  --> $DIR/len_zero.rs:106:8
+  --> $DIR/len_zero.rs:146:8
    |
 LL |     if 0 != has_is_empty.len() {
    |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
 
 error: length comparison to zero
-  --> $DIR/len_zero.rs:109:8
+  --> $DIR/len_zero.rs:149:8
    |
 LL |     if 0 < has_is_empty.len() {
    |        ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
 
 error: length comparison to one
-  --> $DIR/len_zero.rs:112:8
+  --> $DIR/len_zero.rs:152:8
    |
 LL |     if 1 <= has_is_empty.len() {
    |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
 
 error: length comparison to one
-  --> $DIR/len_zero.rs:115:8
+  --> $DIR/len_zero.rs:155:8
    |
 LL |     if 1 > has_is_empty.len() {
    |        ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
 
 error: length comparison to zero
-  --> $DIR/len_zero.rs:129:8
+  --> $DIR/len_zero.rs:169:8
    |
 LL |     if with_is_empty.len() == 0 {
    |        ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()`
 
 error: length comparison to zero
-  --> $DIR/len_zero.rs:142:8
+  --> $DIR/len_zero.rs:182:8
    |
 LL |     if b.len() != 0 {}
    |        ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()`
 
-error: aborting due to 14 previous errors
+error: aborting due to 21 previous errors