-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implemented unnecessary min #12061
implemented unnecessary min #12061
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#![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 | ||
|
||
let mut min = u32::MAX; | ||
for _ in 0..1000 { | ||
min = min.min(random_u32()); // shouldn't lint | ||
} | ||
} | ||
fn test_usize() -> usize { | ||
42 | ||
} | ||
fn test_i64() -> i64 { | ||
42 | ||
} | ||
const fn const_fn(input: i64) -> i64 { | ||
-2 * input | ||
} | ||
fn random_u32() -> u32 { | ||
// random number generator | ||
0 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#![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 | ||
|
||
let mut min = u32::MAX; | ||
for _ in 0..1000 { | ||
min = min.min(random_u32()); // shouldn't lint | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to see tests using the It might be worth adding a test for things like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'll add that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i tried your suggestion and it doesn't lint it. |
||
fn test_usize() -> usize { | ||
42 | ||
} | ||
fn test_i64() -> i64 { | ||
42 | ||
} | ||
const fn const_fn(input: i64) -> i64 { | ||
-2 * input | ||
} | ||
fn random_u32() -> u32 { | ||
// random number generator | ||
0 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know whether clippy has a policy for these sorts of things, but linting using the values of constants makes me wary. It would be possible to change
A
,B
, orC
in such a way that the use ofmin
ormax
do actually do something, especially if it's something likemin(CONST_LIMIT, user_input)
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think i get your point.
In your commented block of code all "variables" are constants so afaik it is not possible that one of them can change.
In the case of
there should be only a lint emitted if
CONST_LIMIT
is either the minimum or maximum value. If it is the minimum value, then the result will always be the minumum regardless of theuser_input
and if it is the maximum value then the result will always be theuser_input
.Or did you mean something like that?:
In this situation nothing should be linted.
I'll add a test to confirm it.
thank you for your feedback. I appreciate it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i added the test