Skip to content
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

Refactor types #6938

Merged
merged 6 commits into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions clippy_lints/src/absurd_extreme_comparisons.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::consts::{constant, Constant};

use clippy_utils::comparisons::{normalize_comparison, Rel};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_isize_or_usize;
use clippy_utils::{clip, int_bits, unsext};

declare_clippy_lint! {
/// **What it does:** Checks for comparisons where one side of the relation is
/// either the minimum or maximum value for its type and warns if it involves a
/// case that is always true or always false. Only integer and boolean types are
/// checked.
///
/// **Why is this bad?** An expression like `min <= x` may misleadingly imply
/// that it is possible for `x` to be less than the minimum. Expressions like
/// `max < x` are probably mistakes.
///
/// **Known problems:** For `usize` the size of the current compile target will
/// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
/// a comparison to detect target pointer width will trigger this lint. One can
/// use `mem::sizeof` and compare its value or conditional compilation
/// attributes
/// like `#[cfg(target_pointer_width = "64")] ..` instead.
///
/// **Example:**
///
/// ```rust
/// let vec: Vec<isize> = Vec::new();
/// if vec.len() <= 0 {}
/// if 100 > i32::MAX {}
/// ```
pub ABSURD_EXTREME_COMPARISONS,
correctness,
"a comparison with a maximum or minimum value that is always true or false"
}

declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);

impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind {
if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
if !expr.span.from_expansion() {
let msg = "this comparison involving the minimum or maximum element for this \
type contains a case that is always true or always false";

let conclusion = match result {
AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
AbsurdComparisonResult::InequalityImpossible => format!(
"the case where the two sides are not equal never occurs, consider using `{} == {}` \
instead",
snippet(cx, lhs.span, "lhs"),
snippet(cx, rhs.span, "rhs")
),
};

let help = format!(
"because `{}` is the {} value for this type, {}",
snippet(cx, culprit.expr.span, "x"),
match culprit.which {
ExtremeType::Minimum => "minimum",
ExtremeType::Maximum => "maximum",
},
conclusion
);

span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
}
}
}
}
}

enum ExtremeType {
Minimum,
Maximum,
}

struct ExtremeExpr<'a> {
which: ExtremeType,
expr: &'a Expr<'a>,
}

enum AbsurdComparisonResult {
AlwaysFalse,
AlwaysTrue,
InequalityImpossible,
}

fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
if let ExprKind::Cast(ref cast_exp, _) = expr.kind {
let precast_ty = cx.typeck_results().expr_ty(cast_exp);
let cast_ty = cx.typeck_results().expr_ty(expr);

return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
}

false
}

fn detect_absurd_comparison<'tcx>(
cx: &LateContext<'tcx>,
op: BinOpKind,
lhs: &'tcx Expr<'_>,
rhs: &'tcx Expr<'_>,
) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
use ExtremeType::{Maximum, Minimum};
// absurd comparison only makes sense on primitive types
// primitive types don't implement comparison operators with each other
if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) {
return None;
}

// comparisons between fix sized types and target sized types are considered unanalyzable
if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
return None;
}

let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;

let lx = detect_extreme_expr(cx, normalized_lhs);
let rx = detect_extreme_expr(cx, normalized_rhs);

Some(match rel {
Rel::Lt => {
match (lx, rx) {
(Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
(_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
_ => return None,
}
},
Rel::Le => {
match (lx, rx) {
(Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
(Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
(_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
(_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
_ => return None,
}
},
Rel::Ne | Rel::Eq => return None,
})
}

fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
let ty = cx.typeck_results().expr_ty(expr);

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

let which = match (ty.kind(), cv) {
(&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
(&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
ExtremeType::Minimum
},

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

_ => return None,
};
Some(ExtremeExpr { which, expr })
}
Loading