Auto merge of #10293 - Alexendoo:bool-assert-comparison-negation, r=dswij
Negate suggestions when needed in `bool_assert_comparison` changelog: none assuming this gets into the same release as #10218 Fixes #10291 r? `@dswij` Thanks to `@black-puppydog` for spotting it early
This commit is contained in:
commit
fd2d8beaf8
4 changed files with 176 additions and 51 deletions
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue