From 7a32c289319bb376d47e7a9f83057f82e1d4fda0 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sat, 16 Jun 2018 18:33:11 +0200 Subject: [PATCH 1/2] Fix #2741 --- clippy_lints/src/minmax.rs | 33 ++++++++++++++++++++++++++++----- tests/ui/min_max.rs | 3 +++ tests/ui/min_max.stderr | 8 ++++---- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 8c511d8f0ad9..1e390b0d8960 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,8 +1,9 @@ use crate::consts::{constant_simple, Constant}; -use rustc::lint::*; +use crate::utils::{match_def_path, opt_def_id, paths, sext, span_lint}; use rustc::hir::*; +use rustc::lint::*; +use rustc::ty::{self, TyCtxt}; use std::cmp::{Ordering, PartialOrd}; -use crate::utils::{match_def_path, opt_def_id, paths, span_lint}; /// **What it does:** Checks for expressions where `std::cmp::min` and `max` are /// used to clamp values, but switched so that the result is constant. @@ -36,14 +37,22 @@ impl LintPass for MinMaxPass { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) { - if let Some((inner_max, inner_c, _)) = min_max(cx, oe) { + if let Some((inner_max, inner_c, ie)) = min_max(cx, oe) { if outer_max == inner_max { return; } - match (outer_max, outer_c.partial_cmp(&inner_c)) { + match ( + outer_max, + const_partial_cmp(cx.tcx, &outer_c, &inner_c, &cx.tables.expr_ty(ie).sty), + ) { (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (), _ => { - span_lint(cx, MIN_MAX, expr.span, "this min/max combination leads to constant result"); + span_lint( + cx, + MIN_MAX, + expr.span, + "this min/max combination leads to constant result", + ); }, } } @@ -51,6 +60,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass { } } +// Constant::partial_cmp incorrectly orders signed integers +fn const_partial_cmp(tcx: TyCtxt, a: &Constant, b: &Constant, expr_ty: &ty::TypeVariants) -> Option { + match *expr_ty { + ty::TyInt(int_ty) => { + if let (&Constant::Int(a), &Constant::Int(b)) = (a, b) { + Some(sext(tcx, a, int_ty).cmp(&sext(tcx, b, int_ty))) + } else { + None + } + }, + _ => a.partial_cmp(&b), + } +} + #[derive(PartialEq, Eq, Debug)] enum MinMax { Min, diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index 1199206e42c3..9b29f73b2ac2 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -23,6 +23,9 @@ fn main() { min(1, max(LARGE, x)); // no error, we don't lookup consts here + let y = 2isize; + min(max(y, -1), 3); + let s; s = "Hello"; diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index de4c4e16fa03..b8ea183fcc94 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -31,15 +31,15 @@ error: this min/max combination leads to constant result | ^^^^^^^^^^^^^^^^^^^^^^^ error: this min/max combination leads to constant result - --> $DIR/min_max.rs:29:5 + --> $DIR/min_max.rs:32:5 | -29 | min("Apple", max("Zoo", s)); +32 | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this min/max combination leads to constant result - --> $DIR/min_max.rs:30:5 + --> $DIR/min_max.rs:33:5 | -30 | max(min(s, "Apple"), "Zoo"); +33 | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 7 previous errors From 0f83c68698db709dfcc64bd9eef22feabde945c9 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 19 Jun 2018 07:37:09 +0200 Subject: [PATCH 2/2] Replace `Constant::partial_cmp` --- clippy_lints/src/consts.rs | 31 +++++++++++++++++++++---------- clippy_lints/src/minmax.rs | 21 +++------------------ 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index c700af1e6e39..36417cd08777 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -122,21 +122,32 @@ impl Hash for Constant { } } -impl PartialOrd for Constant { - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { +impl Constant { + pub fn partial_cmp(tcx: TyCtxt, cmp_type: &ty::TypeVariants, left: &Self, right: &Self) -> Option { + match (left, right) { (&Constant::Str(ref ls), &Constant::Str(ref rs)) => Some(ls.cmp(rs)), (&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)), - (&Constant::Int(l), &Constant::Int(r)) => Some(l.cmp(&r)), + (&Constant::Int(l), &Constant::Int(r)) => { + if let ty::TyInt(int_ty) = *cmp_type { + Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) + } else { + Some(l.cmp(&r)) + } + }, (&Constant::F64(l), &Constant::F64(r)) => l.partial_cmp(&r), (&Constant::F32(l), &Constant::F32(r)) => l.partial_cmp(&r), (&Constant::Bool(ref l), &Constant::Bool(ref r)) => Some(l.cmp(r)), - (&Constant::Tuple(ref l), &Constant::Tuple(ref r)) | (&Constant::Vec(ref l), &Constant::Vec(ref r)) => { - l.partial_cmp(r) - }, - (&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => match lv.partial_cmp(rv) { - Some(Equal) => Some(ls.cmp(rs)), - x => x, + (&Constant::Tuple(ref l), &Constant::Tuple(ref r)) | (&Constant::Vec(ref l), &Constant::Vec(ref r)) => l + .iter() + .zip(r.iter()) + .map(|(li, ri)| Constant::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + (&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => { + match Constant::partial_cmp(tcx, cmp_type, lv, rv) { + Some(Equal) => Some(ls.cmp(rs)), + x => x, + } }, _ => None, // TODO: Are there any useful inter-type orderings? } diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 1e390b0d8960..4be2b9f52271 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,9 +1,8 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{match_def_path, opt_def_id, paths, sext, span_lint}; +use crate::utils::{match_def_path, opt_def_id, paths, span_lint}; use rustc::hir::*; use rustc::lint::*; -use rustc::ty::{self, TyCtxt}; -use std::cmp::{Ordering, PartialOrd}; +use std::cmp::Ordering; /// **What it does:** Checks for expressions where `std::cmp::min` and `max` are /// used to clamp values, but switched so that the result is constant. @@ -43,7 +42,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass { } match ( outer_max, - const_partial_cmp(cx.tcx, &outer_c, &inner_c, &cx.tables.expr_ty(ie).sty), + Constant::partial_cmp(cx.tcx, &cx.tables.expr_ty(ie).sty, &outer_c, &inner_c), ) { (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (), _ => { @@ -60,20 +59,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass { } } -// Constant::partial_cmp incorrectly orders signed integers -fn const_partial_cmp(tcx: TyCtxt, a: &Constant, b: &Constant, expr_ty: &ty::TypeVariants) -> Option { - match *expr_ty { - ty::TyInt(int_ty) => { - if let (&Constant::Int(a), &Constant::Int(b)) = (a, b) { - Some(sext(tcx, a, int_ty).cmp(&sext(tcx, b, int_ty))) - } else { - None - } - }, - _ => a.partial_cmp(&b), - } -} - #[derive(PartialEq, Eq, Debug)] enum MinMax { Min,