fix: implicit_saturating_sub suggests wrongly on untyped int literal
This commit is contained in:
parent
22c13893e6
commit
67a76f9fb5
6 changed files with 57 additions and 9 deletions
|
|
@ -1,9 +1,13 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::{Sugg, make_binop};
|
||||
use clippy_utils::{
|
||||
SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt, sym,
|
||||
SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, is_integer_literal_untyped,
|
||||
peel_blocks, peel_blocks_with_stmt, sym,
|
||||
};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
|
|
@ -238,10 +242,21 @@ fn check_subtraction(
|
|||
if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) {
|
||||
// This part of the condition is voluntarily split from the one before to ensure that
|
||||
// if `snippet_opt` fails, it won't try the next conditions.
|
||||
if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST))
|
||||
&& let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_paren)
|
||||
&& let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr)
|
||||
{
|
||||
if !is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let big_expr_sugg = (if is_integer_literal_untyped(big_expr) {
|
||||
let get_snippet = |span: Span| {
|
||||
let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
|
||||
let big_expr_ty = cx.typeck_results().expr_ty(big_expr);
|
||||
Cow::Owned(format!("{snippet}_{big_expr_ty}"))
|
||||
};
|
||||
Sugg::hir_from_snippet(cx, big_expr, get_snippet)
|
||||
} else {
|
||||
Sugg::hir_with_applicability(cx, big_expr, "..", &mut applicability)
|
||||
})
|
||||
.maybe_paren();
|
||||
let little_expr_sugg = Sugg::hir_with_applicability(cx, little_expr, "..", &mut applicability);
|
||||
|
||||
let sugg = format!(
|
||||
"{}{big_expr_sugg}.saturating_sub({little_expr_sugg}){}",
|
||||
if is_composited { "{ " } else { "" },
|
||||
|
|
@ -254,7 +269,7 @@ fn check_subtraction(
|
|||
"manual arithmetic check found",
|
||||
"replace it with",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
} else if eq_expr_value(cx, left, little_expr)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock};
|
|||
use itertools::Itertools;
|
||||
use rustc_abi::Integer;
|
||||
use rustc_ast::ast::{self, LitKind, RangeLimits};
|
||||
use rustc_ast::join_path_syms;
|
||||
use rustc_ast::{LitIntType, join_path_syms};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::indexmap;
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
|
|
@ -1385,6 +1385,17 @@ pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
/// Checks whether the given expression is an untyped integer literal.
|
||||
pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Lit(spanned) = expr.kind
|
||||
&& let LitKind::Int(_, suffix) = spanned.node
|
||||
{
|
||||
return suffix == LitIntType::Unsuffixed;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks whether the given expression is a constant literal of the given value.
|
||||
pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
|
||||
if let ExprKind::Lit(spanned) = expr.kind
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ impl<'a> Sugg<'a> {
|
|||
|
||||
/// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
|
||||
/// function variants of `Sugg`, since these use different snippet functions.
|
||||
fn hir_from_snippet(
|
||||
pub fn hir_from_snippet(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
mut get_snippet: impl FnMut(Span) -> Cow<'a, str>,
|
||||
|
|
|
|||
|
|
@ -252,3 +252,11 @@ fn arbitrary_expression() {
|
|||
0
|
||||
};
|
||||
}
|
||||
|
||||
fn issue16307() {
|
||||
let x: u8 = 100;
|
||||
let y = 100_u8.saturating_sub(x);
|
||||
//~^ implicit_saturating_sub
|
||||
|
||||
println!("{y}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -326,3 +326,11 @@ fn arbitrary_expression() {
|
|||
0
|
||||
};
|
||||
}
|
||||
|
||||
fn issue16307() {
|
||||
let x: u8 = 100;
|
||||
let y = if x >= 100 { 0 } else { 100 - x };
|
||||
//~^ implicit_saturating_sub
|
||||
|
||||
println!("{y}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,5 +238,11 @@ error: manual arithmetic check found
|
|||
LL | let _ = if a < b * 2 { 0 } else { a - b * 2 };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)`
|
||||
|
||||
error: aborting due to 27 previous errors
|
||||
error: manual arithmetic check found
|
||||
--> tests/ui/implicit_saturating_sub.rs:332:13
|
||||
|
|
||||
LL | let y = if x >= 100 { 0 } else { 100 - x };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `100_u8.saturating_sub(x)`
|
||||
|
||||
error: aborting due to 28 previous errors
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue