Skip to content

Commit

Permalink
implemented unnecessary min
Browse files Browse the repository at this point in the history
  • Loading branch information
FelixMaetzler committed Dec 30, 2023
1 parent c6aeb28 commit 49cbc0f
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5669,6 +5669,7 @@ Released 2018-09-13
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
[`unnecessary_min`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Expand Up @@ -448,6 +448,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::UNNECESSARY_JOIN_INFO,
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
crate::methods::UNNECESSARY_MIN_INFO,
crate::methods::UNNECESSARY_SORT_BY_INFO,
crate::methods::UNNECESSARY_TO_OWNED_INFO,
crate::methods::UNWRAP_OR_DEFAULT_INFO,
Expand Down
24 changes: 24 additions & 0 deletions clippy_lints/src/methods/mod.rs
Expand Up @@ -111,6 +111,7 @@ mod unnecessary_iter_cloned;
mod unnecessary_join;
mod unnecessary_lazy_eval;
mod unnecessary_literal_unwrap;
mod unnecessary_min;
mod unnecessary_sort_by;
mod unnecessary_to_owned;
mod unwrap_expect_used;
Expand Down Expand Up @@ -3856,6 +3857,27 @@ declare_clippy_lint! {
"using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for unnecessary calls to `min()`
///
/// ### Why is this bad?
///
/// In these cases it is not necessary to call `min()`
/// ### Example
/// ```no_run
/// let _ = 0.min(7_u32);
/// ```
/// Use instead:
/// ```no_run
/// let _ = 7;
/// ```
#[clippy::version = "1.77.0"]
pub UNNECESSARY_MIN,
complexity,
"using 'min()' when there is no need for it"
}

pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
Expand Down Expand Up @@ -4011,6 +4033,7 @@ impl_lint_pass!(Methods => [
ITER_FILTER_IS_SOME,
ITER_FILTER_IS_OK,
MANUAL_IS_VARIANT_AND,
UNNECESSARY_MIN,
]);

/// Extracts a method call name, args, and `Span` of the method name.
Expand Down Expand Up @@ -4292,6 +4315,7 @@ impl Methods {
Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
_ => {},
},
("min", [arg]) => unnecessary_min::check(cx, expr, recv, arg),
("drain", ..) => {
if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id)
&& matches!(kind, StmtKind::Semi(_))
Expand Down
111 changes: 111 additions & 0 deletions clippy_lints/src/methods/unnecessary_min.rs
@@ -0,0 +1,111 @@
use std::cmp::Ordering;

use super::UNNECESSARY_MIN;
use clippy_utils::diagnostics::span_lint_and_sugg;

use clippy_utils::consts::{constant, Constant};
use clippy_utils::source::snippet;
use clippy_utils::{clip, int_bits, unsext};
use hir::Expr;

use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;

use rustc_middle::ty;
use rustc_span::Span;

pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
if both_are_constant(cx, expr, recv, arg) {
return;
}
one_extrema(cx, expr, recv, arg);
}
fn lint(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Span, other: Span) {
let msg = format!(
"`{}` is never greater than `{}` and has therefore no effect",
snippet(cx, sugg, "Not yet implemented"),
snippet(cx, other, "Not yet implemented")
);
span_lint_and_sugg(
cx,
UNNECESSARY_MIN,
expr.span,
&msg,
"try",
snippet(cx, sugg, "Not yet implemented").to_string(),
Applicability::MachineApplicable,
);
}

fn try_to_eval<'tcx>(
cx: &LateContext<'tcx>,
recv: &'tcx Expr<'_>,
arg: &'tcx Expr<'_>,
) -> (Option<Constant<'tcx>>, Option<Constant<'tcx>>) {
(
(constant(cx, cx.typeck_results(), recv)),
(constant(cx, cx.typeck_results(), arg)),
)
}
#[derive(Debug)]
enum Extrema {
Minimum,
Maximum,
}
fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Extrema> {
let ty = cx.typeck_results().expr_ty(expr);

let cv = constant(cx, cx.typeck_results(), expr)?;

match (ty.kind(), cv) {
(&ty::Uint(_), Constant::Int(0)) => Some(Extrema::Minimum),
(&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
Some(Extrema::Minimum)
},

(&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
Some(Extrema::Maximum)
},
(&ty::Uint(uty), Constant::Int(i)) if i == clip(cx.tcx, u128::MAX, uty) => Some(Extrema::Maximum),

_ => None,
}
}
fn both_are_constant<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
recv: &'tcx Expr<'_>,
arg: &'tcx Expr<'_>,
) -> bool {
let ty = cx.typeck_results().expr_ty(recv);
if let (Some(left), Some(right)) = try_to_eval(cx, recv, arg)
&& let Some(ord) = Constant::partial_cmp(cx.tcx, ty, &left, &right)
{
let (sugg, other) = match ord {
Ordering::Less => (recv.span, arg.span),
Ordering::Equal | Ordering::Greater => (arg.span, recv.span),
};

lint(cx, expr, sugg, other);
return true;
}
false
}
fn one_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) -> bool {
if let Some(extrema) = detect_extrema(cx, recv) {
match extrema {
Extrema::Minimum => lint(cx, expr, recv.span, arg.span),
Extrema::Maximum => lint(cx, expr, arg.span, recv.span),
}
return true;
} else if let Some(extrema) = detect_extrema(cx, arg) {
match extrema {
Extrema::Minimum => lint(cx, expr, arg.span, recv.span),
Extrema::Maximum => lint(cx, expr, recv.span, arg.span),
}
return true;
}

false
}
11 changes: 10 additions & 1 deletion tests/ui/cast.stderr
Expand Up @@ -333,6 +333,15 @@ help: ... or use `try_from` and handle the error accordingly
LL | i8::try_from((-99999999999i64).min(1));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

error: `(-99999999999i64)` is never greater than `1` and has therefore no effect
--> $DIR/cast.rs:179:5
|
LL | (-99999999999i64).min(1) as i8;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(-99999999999i64)`
|
= note: `-D clippy::unnecessary-min` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_min)]`

error: casting `u64` to `u8` may truncate the value
--> $DIR/cast.rs:193:5
|
Expand Down Expand Up @@ -444,5 +453,5 @@ help: ... or use `try_from` and handle the error accordingly
LL | let c = u8::try_from(q / 1000);
| ~~~~~~~~~~~~~~~~~~~~~~

error: aborting due to 51 previous errors
error: aborting due to 52 previous errors

45 changes: 45 additions & 0 deletions tests/ui/unnecessary_min.fixed
@@ -0,0 +1,45 @@
#![allow(unused)]
#![warn(clippy::unnecessary_min)]

fn main() {
const A: i64 = 45;
const B: i64 = -1;
const C: i64 = const_fn(B);
let _ = (A * B); // Both are constants
let _ = B; // Both are constants
let _ = B; // Both are constants

let _ = (-6_i32); // Both are Literals
let _ = 6; // Both are Literals

let _ = 6; // Both are Literals

let _ = 0; // unsigned with zero
let _ = 0_u32; // unsigned with zero

let _ = i32::MIN; // singed MIN
let _ = i32::MIN; // singed MIN

let _ = 42; // singed MAX
let _ = 42; // singed MAX

let _ = 0; // unsigned with zero and function

let _ = 0; // unsigned with zero and function

let _ = i64::MIN; // signed with MIN and function

let _ = i64::MIN; // signed with MIN and function

let _ = test_i64(); // signed with MAX and function
let _ = test_i64(); // signed with MAX and function
}
fn test_usize() -> usize {
42
}
fn test_i64() -> i64 {
42
}
const fn const_fn(input: i64) -> i64 {
-2 * input
}
45 changes: 45 additions & 0 deletions tests/ui/unnecessary_min.rs
@@ -0,0 +1,45 @@
#![allow(unused)]
#![warn(clippy::unnecessary_min)]

fn main() {
const A: i64 = 45;
const B: i64 = -1;
const C: i64 = const_fn(B);
let _ = (A * B).min(B); // Both are constants
let _ = C.min(B); // Both are constants
let _ = B.min(C); // Both are constants

let _ = (-6_i32).min(9); // Both are Literals
let _ = 9_u32.min(6); // Both are Literals

let _ = 6.min(7_u8); // Both are Literals

let _ = 0.min(7_u8); // unsigned with zero
let _ = 7.min(0_u32); // unsigned with zero

let _ = i32::MIN.min(42); // singed MIN
let _ = 42.min(i32::MIN); // singed MIN

let _ = i32::MAX.min(42); // singed MAX
let _ = 42.min(i32::MAX); // singed MAX

let _ = 0.min(test_usize()); // unsigned with zero and function

let _ = test_usize().min(0); // unsigned with zero and function

let _ = i64::MIN.min(test_i64()); // signed with MIN and function

let _ = test_i64().min(i64::MIN); // signed with MIN and function

let _ = i64::MAX.min(test_i64()); // signed with MAX and function
let _ = test_i64().min(i64::MAX); // signed with MAX and function
}
fn test_usize() -> usize {
42
}
fn test_i64() -> i64 {
42
}
const fn const_fn(input: i64) -> i64 {
-2 * input
}

0 comments on commit 49cbc0f

Please sign in to comment.