Negate suggestions when needed in `bool_assert_comparison`

This commit is contained in:
Alex Macleod 2023-02-04 19:12:06 +00:00
parent 8a9860901f
commit 5546c82051
4 changed files with 176 additions and 51 deletions

View File

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_copy};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -34,14 +35,16 @@ declare_clippy_lint! {
declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
fn is_bool_lit(e: &Expr<'_>) -> bool {
matches!(
e.kind,
ExprKind::Lit(Lit {
node: LitKind::Bool(_),
..
})
) && !e.span.from_expansion()
fn extract_bool_lit(e: &Expr<'_>) -> Option<bool> {
if let ExprKind::Lit(Lit {
node: LitKind::Bool(b), ..
}) = e.kind
&& !e.span.from_expansion()
{
Some(b)
} else {
None
}
}
fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
@ -69,24 +72,23 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
let macro_name = cx.tcx.item_name(macro_call.def_id);
if !matches!(
macro_name.as_str(),
"assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne"
) {
return;
}
let eq_macro = match macro_name.as_str() {
"assert_eq" | "debug_assert_eq" => true,
"assert_ne" | "debug_assert_ne" => false,
_ => return,
};
let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
let a_span = a.span.source_callsite();
let b_span = b.span.source_callsite();
let (lit_span, non_lit_expr) = match (is_bool_lit(a), is_bool_lit(b)) {
// assert_eq!(true, b)
// ^^^^^^
(true, false) => (a_span.until(b_span), b),
// assert_eq!(a, true)
// ^^^^^^
(false, true) => (b_span.with_lo(a_span.hi()), a),
let (lit_span, bool_value, non_lit_expr) = match (extract_bool_lit(a), extract_bool_lit(b)) {
// assert_eq!(true/false, b)
// ^^^^^^^^^^^^
(Some(bool_value), None) => (a_span.until(b_span), bool_value, b),
// assert_eq!(a, true/false)
// ^^^^^^^^^^^^
(None, Some(bool_value)) => (b_span.with_lo(a_span.hi()), bool_value, a),
// If there are two boolean arguments, we definitely don't understand
// what's going on, so better leave things as is...
//
@ -121,9 +123,16 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
// ^^^^^^^^^
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())];
if bool_value ^ eq_macro {
let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else { return };
suggestions.push((non_lit_expr.span, (!sugg).to_string()));
}
diag.multipart_suggestion(
format!("replace it with `{non_eq_mac}!(..)`"),
vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())],
suggestions,
Applicability::MachineApplicable,
);
},

View File

@ -86,7 +86,7 @@ fn main() {
let b = ImplNotTraitWithBool;
assert_eq!("a".len(), 1);
assert!("a".is_empty());
assert!(!"a".is_empty());
assert!("".is_empty());
assert!("".is_empty());
assert_eq!(a!(), b!());
@ -97,16 +97,16 @@ fn main() {
assert_ne!("a".len(), 1);
assert!("a".is_empty());
assert!("".is_empty());
assert!("".is_empty());
assert!(!"".is_empty());
assert!(!"".is_empty());
assert_ne!(a!(), b!());
assert_ne!(a!(), "".is_empty());
assert_ne!("".is_empty(), b!());
assert_ne!(a, true);
assert!(b);
assert!(!b);
debug_assert_eq!("a".len(), 1);
debug_assert!("a".is_empty());
debug_assert!(!"a".is_empty());
debug_assert!("".is_empty());
debug_assert!("".is_empty());
debug_assert_eq!(a!(), b!());
@ -117,27 +117,27 @@ fn main() {
debug_assert_ne!("a".len(), 1);
debug_assert!("a".is_empty());
debug_assert!("".is_empty());
debug_assert!("".is_empty());
debug_assert!(!"".is_empty());
debug_assert!(!"".is_empty());
debug_assert_ne!(a!(), b!());
debug_assert_ne!(a!(), "".is_empty());
debug_assert_ne!("".is_empty(), b!());
debug_assert_ne!(a, true);
debug_assert!(b);
debug_assert!(!b);
// assert with error messages
assert_eq!("a".len(), 1, "tadam {}", 1);
assert_eq!("a".len(), 1, "tadam {}", true);
assert!("a".is_empty(), "tadam {}", 1);
assert!("a".is_empty(), "tadam {}", true);
assert!("a".is_empty(), "tadam {}", true);
assert!(!"a".is_empty(), "tadam {}", 1);
assert!(!"a".is_empty(), "tadam {}", true);
assert!(!"a".is_empty(), "tadam {}", true);
assert_eq!(a, true, "tadam {}", false);
debug_assert_eq!("a".len(), 1, "tadam {}", 1);
debug_assert_eq!("a".len(), 1, "tadam {}", true);
debug_assert!("a".is_empty(), "tadam {}", 1);
debug_assert!("a".is_empty(), "tadam {}", true);
debug_assert!("a".is_empty(), "tadam {}", true);
debug_assert!(!"a".is_empty(), "tadam {}", 1);
debug_assert!(!"a".is_empty(), "tadam {}", true);
debug_assert!(!"a".is_empty(), "tadam {}", true);
debug_assert_eq!(a, true, "tadam {}", false);
assert!(a!());
@ -158,4 +158,14 @@ fn main() {
}};
}
in_macro!(a);
assert!("".is_empty());
assert!("".is_empty());
assert!(!"requires negation".is_empty());
assert!(!"requires negation".is_empty());
debug_assert!("".is_empty());
debug_assert!("".is_empty());
debug_assert!(!"requires negation".is_empty());
debug_assert!(!"requires negation".is_empty());
}

View File

@ -158,4 +158,14 @@ fn main() {
}};
}
in_macro!(a);
assert_eq!("".is_empty(), true);
assert_ne!("".is_empty(), false);
assert_ne!("requires negation".is_empty(), true);
assert_eq!("requires negation".is_empty(), false);
debug_assert_eq!("".is_empty(), true);
debug_assert_ne!("".is_empty(), false);
debug_assert_ne!("requires negation".is_empty(), true);
debug_assert_eq!("requires negation".is_empty(), false);
}

View File

@ -8,7 +8,7 @@ LL | assert_eq!("a".is_empty(), false);
help: replace it with `assert!(..)`
|
LL - assert_eq!("a".is_empty(), false);
LL + assert!("a".is_empty());
LL + assert!(!"a".is_empty());
|
error: used `assert_eq!` with a literal bool
@ -68,7 +68,7 @@ LL | assert_ne!("".is_empty(), true);
help: replace it with `assert!(..)`
|
LL - assert_ne!("".is_empty(), true);
LL + assert!("".is_empty());
LL + assert!(!"".is_empty());
|
error: used `assert_ne!` with a literal bool
@ -80,7 +80,7 @@ LL | assert_ne!(true, "".is_empty());
help: replace it with `assert!(..)`
|
LL - assert_ne!(true, "".is_empty());
LL + assert!("".is_empty());
LL + assert!(!"".is_empty());
|
error: used `assert_ne!` with a literal bool
@ -92,7 +92,7 @@ LL | assert_ne!(b, true);
help: replace it with `assert!(..)`
|
LL - assert_ne!(b, true);
LL + assert!(b);
LL + assert!(!b);
|
error: used `debug_assert_eq!` with a literal bool
@ -104,7 +104,7 @@ LL | debug_assert_eq!("a".is_empty(), false);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("a".is_empty(), false);
LL + debug_assert!("a".is_empty());
LL + debug_assert!(!"a".is_empty());
|
error: used `debug_assert_eq!` with a literal bool
@ -164,7 +164,7 @@ LL | debug_assert_ne!("".is_empty(), true);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!("".is_empty(), true);
LL + debug_assert!("".is_empty());
LL + debug_assert!(!"".is_empty());
|
error: used `debug_assert_ne!` with a literal bool
@ -176,7 +176,7 @@ LL | debug_assert_ne!(true, "".is_empty());
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!(true, "".is_empty());
LL + debug_assert!("".is_empty());
LL + debug_assert!(!"".is_empty());
|
error: used `debug_assert_ne!` with a literal bool
@ -188,7 +188,7 @@ LL | debug_assert_ne!(b, true);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!(b, true);
LL + debug_assert!(b);
LL + debug_assert!(!b);
|
error: used `assert_eq!` with a literal bool
@ -200,7 +200,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", 1);
help: replace it with `assert!(..)`
|
LL - assert_eq!("a".is_empty(), false, "tadam {}", 1);
LL + assert!("a".is_empty(), "tadam {}", 1);
LL + assert!(!"a".is_empty(), "tadam {}", 1);
|
error: used `assert_eq!` with a literal bool
@ -212,7 +212,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", true);
help: replace it with `assert!(..)`
|
LL - assert_eq!("a".is_empty(), false, "tadam {}", true);
LL + assert!("a".is_empty(), "tadam {}", true);
LL + assert!(!"a".is_empty(), "tadam {}", true);
|
error: used `assert_eq!` with a literal bool
@ -224,7 +224,7 @@ LL | assert_eq!(false, "a".is_empty(), "tadam {}", true);
help: replace it with `assert!(..)`
|
LL - assert_eq!(false, "a".is_empty(), "tadam {}", true);
LL + assert!("a".is_empty(), "tadam {}", true);
LL + assert!(!"a".is_empty(), "tadam {}", true);
|
error: used `debug_assert_eq!` with a literal bool
@ -236,7 +236,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
LL + debug_assert!("a".is_empty(), "tadam {}", 1);
LL + debug_assert!(!"a".is_empty(), "tadam {}", 1);
|
error: used `debug_assert_eq!` with a literal bool
@ -248,7 +248,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
LL + debug_assert!("a".is_empty(), "tadam {}", true);
LL + debug_assert!(!"a".is_empty(), "tadam {}", true);
|
error: used `debug_assert_eq!` with a literal bool
@ -260,7 +260,7 @@ LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
LL + debug_assert!("a".is_empty(), "tadam {}", true);
LL + debug_assert!(!"a".is_empty(), "tadam {}", true);
|
error: used `assert_eq!` with a literal bool
@ -299,5 +299,101 @@ LL - renamed!(b, true);
LL + debug_assert!(b);
|
error: aborting due to 25 previous errors
error: used `assert_eq!` with a literal bool
--> $DIR/bool_assert_comparison.rs:162:5
|
LL | assert_eq!("".is_empty(), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_eq!("".is_empty(), true);
LL + assert!("".is_empty());
|
error: used `assert_ne!` with a literal bool
--> $DIR/bool_assert_comparison.rs:163:5
|
LL | assert_ne!("".is_empty(), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_ne!("".is_empty(), false);
LL + assert!("".is_empty());
|
error: used `assert_ne!` with a literal bool
--> $DIR/bool_assert_comparison.rs:164:5
|
LL | assert_ne!("requires negation".is_empty(), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_ne!("requires negation".is_empty(), true);
LL + assert!(!"requires negation".is_empty());
|
error: used `assert_eq!` with a literal bool
--> $DIR/bool_assert_comparison.rs:165:5
|
LL | assert_eq!("requires negation".is_empty(), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_eq!("requires negation".is_empty(), false);
LL + assert!(!"requires negation".is_empty());
|
error: used `debug_assert_eq!` with a literal bool
--> $DIR/bool_assert_comparison.rs:167:5
|
LL | debug_assert_eq!("".is_empty(), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("".is_empty(), true);
LL + debug_assert!("".is_empty());
|
error: used `debug_assert_ne!` with a literal bool
--> $DIR/bool_assert_comparison.rs:168:5
|
LL | debug_assert_ne!("".is_empty(), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!("".is_empty(), false);
LL + debug_assert!("".is_empty());
|
error: used `debug_assert_ne!` with a literal bool
--> $DIR/bool_assert_comparison.rs:169:5
|
LL | debug_assert_ne!("requires negation".is_empty(), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!("requires negation".is_empty(), true);
LL + debug_assert!(!"requires negation".is_empty());
|
error: used `debug_assert_eq!` with a literal bool
--> $DIR/bool_assert_comparison.rs:170:5
|
LL | debug_assert_eq!("requires negation".is_empty(), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("requires negation".is_empty(), false);
LL + debug_assert!(!"requires negation".is_empty());
|
error: aborting due to 33 previous errors