Skip to content

Commit 3749e24

Browse files
author
FelixMaetzler
committed
implemented unnecessary min
1 parent e1dbafd commit 3749e24

File tree

8 files changed

+350
-1
lines changed

8 files changed

+350
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5671,6 +5671,7 @@ Released 2018-09-13
56715671
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
56725672
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
56735673
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
5674+
[`unnecessary_min`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min
56745675
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
56755676
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
56765677
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
450450
crate::methods::UNNECESSARY_JOIN_INFO,
451451
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
452452
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
453+
crate::methods::UNNECESSARY_MIN_INFO,
453454
crate::methods::UNNECESSARY_SORT_BY_INFO,
454455
crate::methods::UNNECESSARY_TO_OWNED_INFO,
455456
crate::methods::UNWRAP_OR_DEFAULT_INFO,

clippy_lints/src/methods/mod.rs

+24
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ mod unnecessary_iter_cloned;
112112
mod unnecessary_join;
113113
mod unnecessary_lazy_eval;
114114
mod unnecessary_literal_unwrap;
115+
mod unnecessary_min;
115116
mod unnecessary_sort_by;
116117
mod unnecessary_to_owned;
117118
mod unwrap_expect_used;
@@ -3887,6 +3888,27 @@ declare_clippy_lint! {
38873888
"splitting a trimmed string at hard-coded newlines"
38883889
}
38893890

3891+
declare_clippy_lint! {
3892+
/// ### What it does
3893+
/// Checks for unnecessary calls to `min()`
3894+
///
3895+
/// ### Why is this bad?
3896+
///
3897+
/// In these cases it is not necessary to call `min()`
3898+
/// ### Example
3899+
/// ```no_run
3900+
/// let _ = 0.min(7_u32);
3901+
/// ```
3902+
/// Use instead:
3903+
/// ```no_run
3904+
/// let _ = 7;
3905+
/// ```
3906+
#[clippy::version = "1.77.0"]
3907+
pub UNNECESSARY_MIN,
3908+
complexity,
3909+
"using 'min()' when there is no need for it"
3910+
}
3911+
38903912
pub struct Methods {
38913913
avoid_breaking_exported_api: bool,
38923914
msrv: Msrv,
@@ -4043,6 +4065,7 @@ impl_lint_pass!(Methods => [
40434065
ITER_FILTER_IS_OK,
40444066
MANUAL_IS_VARIANT_AND,
40454067
STR_SPLIT_AT_NEWLINE,
4068+
UNNECESSARY_MIN,
40464069
]);
40474070

40484071
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4324,6 +4347,7 @@ impl Methods {
43244347
Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
43254348
_ => {},
43264349
},
4350+
("min", [arg]) => unnecessary_min::check(cx, expr, recv, arg),
43274351
("drain", ..) => {
43284352
if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id)
43294353
&& matches!(kind, StmtKind::Semi(_))
+111
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

+10-1
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

+45
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

+45
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)