forked from rust-lang/rust
-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#6938 - Y-Nak:refactor-types, r=flip1995
Refactor types r? `@flip1995` This is the last PR to close rust-lang#6724 🎉 Also, this fixes rust-lang#6936. changelog: `vec_box`: Fix FN in `const` or `static` changelog: `linkedlist`: Fix FN in `const` or `static` changelog: `option_option`: Fix FN in `const` or `static`
- Loading branch information
Showing
15 changed files
with
1,077 additions
and
1,000 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }) | ||
} |
Oops, something went wrong.