Skip to content

Commit 49cbc0f

Browse files
author
FelixMaetzler
committed
implemented unnecessary min
1 parent c6aeb28 commit 49cbc0f

File tree

8 files changed

+350
-1
lines changed

8 files changed

+350
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5669,6 +5669,7 @@ Released 2018-09-13
56695669
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
56705670
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
56715671
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
5672+
[`unnecessary_min`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min
56725673
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
56735674
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
56745675
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
448448
crate::methods::UNNECESSARY_JOIN_INFO,
449449
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
450450
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
451+
crate::methods::UNNECESSARY_MIN_INFO,
451452
crate::methods::UNNECESSARY_SORT_BY_INFO,
452453
crate::methods::UNNECESSARY_TO_OWNED_INFO,
453454
crate::methods::UNWRAP_OR_DEFAULT_INFO,

clippy_lints/src/methods/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ mod unnecessary_iter_cloned;
111111
mod unnecessary_join;
112112
mod unnecessary_lazy_eval;
113113
mod unnecessary_literal_unwrap;
114+
mod unnecessary_min;
114115
mod unnecessary_sort_by;
115116
mod unnecessary_to_owned;
116117
mod unwrap_expect_used;
@@ -3856,6 +3857,27 @@ declare_clippy_lint! {
38563857
"using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`"
38573858
}
38583859

3860+
declare_clippy_lint! {
3861+
/// ### What it does
3862+
/// Checks for unnecessary calls to `min()`
3863+
///
3864+
/// ### Why is this bad?
3865+
///
3866+
/// In these cases it is not necessary to call `min()`
3867+
/// ### Example
3868+
/// ```no_run
3869+
/// let _ = 0.min(7_u32);
3870+
/// ```
3871+
/// Use instead:
3872+
/// ```no_run
3873+
/// let _ = 7;
3874+
/// ```
3875+
#[clippy::version = "1.77.0"]
3876+
pub UNNECESSARY_MIN,
3877+
complexity,
3878+
"using 'min()' when there is no need for it"
3879+
}
3880+
38593881
pub struct Methods {
38603882
avoid_breaking_exported_api: bool,
38613883
msrv: Msrv,
@@ -4011,6 +4033,7 @@ impl_lint_pass!(Methods => [
40114033
ITER_FILTER_IS_SOME,
40124034
ITER_FILTER_IS_OK,
40134035
MANUAL_IS_VARIANT_AND,
4036+
UNNECESSARY_MIN,
40144037
]);
40154038

40164039
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4292,6 +4315,7 @@ impl Methods {
42924315
Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
42934316
_ => {},
42944317
},
4318+
("min", [arg]) => unnecessary_min::check(cx, expr, recv, arg),
42954319
("drain", ..) => {
42964320
if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id)
42974321
&& matches!(kind, StmtKind::Semi(_))
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use std::cmp::Ordering;
2+
3+
use super::UNNECESSARY_MIN;
4+
use clippy_utils::diagnostics::span_lint_and_sugg;
5+
6+
use clippy_utils::consts::{constant, Constant};
7+
use clippy_utils::source::snippet;
8+
use clippy_utils::{clip, int_bits, unsext};
9+
use hir::Expr;
10+
11+
use rustc_errors::Applicability;
12+
use rustc_hir as hir;
13+
use rustc_lint::LateContext;
14+
15+
use rustc_middle::ty;
16+
use rustc_span::Span;
17+
18+
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
19+
if both_are_constant(cx, expr, recv, arg) {
20+
return;
21+
}
22+
one_extrema(cx, expr, recv, arg);
23+
}
24+
fn lint(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Span, other: Span) {
25+
let msg = format!(
26+
"`{}` is never greater than `{}` and has therefore no effect",
27+
snippet(cx, sugg, "Not yet implemented"),
28+
snippet(cx, other, "Not yet implemented")
29+
);
30+
span_lint_and_sugg(
31+
cx,
32+
UNNECESSARY_MIN,
33+
expr.span,
34+
&msg,
35+
"try",
36+
snippet(cx, sugg, "Not yet implemented").to_string(),
37+
Applicability::MachineApplicable,
38+
);
39+
}
40+
41+
fn try_to_eval<'tcx>(
42+
cx: &LateContext<'tcx>,
43+
recv: &'tcx Expr<'_>,
44+
arg: &'tcx Expr<'_>,
45+
) -> (Option<Constant<'tcx>>, Option<Constant<'tcx>>) {
46+
(
47+
(constant(cx, cx.typeck_results(), recv)),
48+
(constant(cx, cx.typeck_results(), arg)),
49+
)
50+
}
51+
#[derive(Debug)]
52+
enum Extrema {
53+
Minimum,
54+
Maximum,
55+
}
56+
fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Extrema> {
57+
let ty = cx.typeck_results().expr_ty(expr);
58+
59+
let cv = constant(cx, cx.typeck_results(), expr)?;
60+
61+
match (ty.kind(), cv) {
62+
(&ty::Uint(_), Constant::Int(0)) => Some(Extrema::Minimum),
63+
(&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
64+
Some(Extrema::Minimum)
65+
},
66+
67+
(&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
68+
Some(Extrema::Maximum)
69+
},
70+
(&ty::Uint(uty), Constant::Int(i)) if i == clip(cx.tcx, u128::MAX, uty) => Some(Extrema::Maximum),
71+
72+
_ => None,
73+
}
74+
}
75+
fn both_are_constant<'tcx>(
76+
cx: &LateContext<'tcx>,
77+
expr: &'tcx Expr<'_>,
78+
recv: &'tcx Expr<'_>,
79+
arg: &'tcx Expr<'_>,
80+
) -> bool {
81+
let ty = cx.typeck_results().expr_ty(recv);
82+
if let (Some(left), Some(right)) = try_to_eval(cx, recv, arg)
83+
&& let Some(ord) = Constant::partial_cmp(cx.tcx, ty, &left, &right)
84+
{
85+
let (sugg, other) = match ord {
86+
Ordering::Less => (recv.span, arg.span),
87+
Ordering::Equal | Ordering::Greater => (arg.span, recv.span),
88+
};
89+
90+
lint(cx, expr, sugg, other);
91+
return true;
92+
}
93+
false
94+
}
95+
fn one_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) -> bool {
96+
if let Some(extrema) = detect_extrema(cx, recv) {
97+
match extrema {
98+
Extrema::Minimum => lint(cx, expr, recv.span, arg.span),
99+
Extrema::Maximum => lint(cx, expr, arg.span, recv.span),
100+
}
101+
return true;
102+
} else if let Some(extrema) = detect_extrema(cx, arg) {
103+
match extrema {
104+
Extrema::Minimum => lint(cx, expr, arg.span, recv.span),
105+
Extrema::Maximum => lint(cx, expr, recv.span, arg.span),
106+
}
107+
return true;
108+
}
109+
110+
false
111+
}

tests/ui/cast.stderr

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,15 @@ help: ... or use `try_from` and handle the error accordingly
333333
LL | i8::try_from((-99999999999i64).min(1));
334334
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
335335

336+
error: `(-99999999999i64)` is never greater than `1` and has therefore no effect
337+
--> $DIR/cast.rs:179:5
338+
|
339+
LL | (-99999999999i64).min(1) as i8;
340+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(-99999999999i64)`
341+
|
342+
= note: `-D clippy::unnecessary-min` implied by `-D warnings`
343+
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_min)]`
344+
336345
error: casting `u64` to `u8` may truncate the value
337346
--> $DIR/cast.rs:193:5
338347
|
@@ -444,5 +453,5 @@ help: ... or use `try_from` and handle the error accordingly
444453
LL | let c = u8::try_from(q / 1000);
445454
| ~~~~~~~~~~~~~~~~~~~~~~
446455

447-
error: aborting due to 51 previous errors
456+
error: aborting due to 52 previous errors
448457

tests/ui/unnecessary_min.fixed

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![allow(unused)]
2+
#![warn(clippy::unnecessary_min)]
3+
4+
fn main() {
5+
const A: i64 = 45;
6+
const B: i64 = -1;
7+
const C: i64 = const_fn(B);
8+
let _ = (A * B); // Both are constants
9+
let _ = B; // Both are constants
10+
let _ = B; // Both are constants
11+
12+
let _ = (-6_i32); // Both are Literals
13+
let _ = 6; // Both are Literals
14+
15+
let _ = 6; // Both are Literals
16+
17+
let _ = 0; // unsigned with zero
18+
let _ = 0_u32; // unsigned with zero
19+
20+
let _ = i32::MIN; // singed MIN
21+
let _ = i32::MIN; // singed MIN
22+
23+
let _ = 42; // singed MAX
24+
let _ = 42; // singed MAX
25+
26+
let _ = 0; // unsigned with zero and function
27+
28+
let _ = 0; // unsigned with zero and function
29+
30+
let _ = i64::MIN; // signed with MIN and function
31+
32+
let _ = i64::MIN; // signed with MIN and function
33+
34+
let _ = test_i64(); // signed with MAX and function
35+
let _ = test_i64(); // signed with MAX and function
36+
}
37+
fn test_usize() -> usize {
38+
42
39+
}
40+
fn test_i64() -> i64 {
41+
42
42+
}
43+
const fn const_fn(input: i64) -> i64 {
44+
-2 * input
45+
}

tests/ui/unnecessary_min.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![allow(unused)]
2+
#![warn(clippy::unnecessary_min)]
3+
4+
fn main() {
5+
const A: i64 = 45;
6+
const B: i64 = -1;
7+
const C: i64 = const_fn(B);
8+
let _ = (A * B).min(B); // Both are constants
9+
let _ = C.min(B); // Both are constants
10+
let _ = B.min(C); // Both are constants
11+
12+
let _ = (-6_i32).min(9); // Both are Literals
13+
let _ = 9_u32.min(6); // Both are Literals
14+
15+
let _ = 6.min(7_u8); // Both are Literals
16+
17+
let _ = 0.min(7_u8); // unsigned with zero
18+
let _ = 7.min(0_u32); // unsigned with zero
19+
20+
let _ = i32::MIN.min(42); // singed MIN
21+
let _ = 42.min(i32::MIN); // singed MIN
22+
23+
let _ = i32::MAX.min(42); // singed MAX
24+
let _ = 42.min(i32::MAX); // singed MAX
25+
26+
let _ = 0.min(test_usize()); // unsigned with zero and function
27+
28+
let _ = test_usize().min(0); // unsigned with zero and function
29+
30+
let _ = i64::MIN.min(test_i64()); // signed with MIN and function
31+
32+
let _ = test_i64().min(i64::MIN); // signed with MIN and function
33+
34+
let _ = i64::MAX.min(test_i64()); // signed with MAX and function
35+
let _ = test_i64().min(i64::MAX); // signed with MAX and function
36+
}
37+
fn test_usize() -> usize {
38+
42
39+
}
40+
fn test_i64() -> i64 {
41+
42
42+
}
43+
const fn const_fn(input: i64) -> i64 {
44+
-2 * input
45+
}

0 commit comments

Comments
 (0)