feat: add and implement unchecked_duration_subtraction lint

This commit is contained in:
Nadir Fejzic 2022-10-01 15:58:10 +02:00
parent 432baf7026
commit 708c2d95c1
4 changed files with 111 additions and 1 deletions

View File

@ -4428,6 +4428,7 @@ Released 2018-09-13
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction
[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc

View File

@ -279,6 +279,7 @@ mod trailing_empty_array;
mod trait_bounds;
mod transmute;
mod types;
mod unchecked_duration_subtraction;
mod undocumented_unsafe_blocks;
mod unicode;
mod uninit_vec;
@ -921,6 +922,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
store.register_late_pass(move || Box::new(unchecked_duration_subtraction::UncheckedDurationSubtraction::new(msrv)));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View File

@ -0,0 +1,107 @@
use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty};
use rustc_errors::Applicability;
use rustc_hir::*;
use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
/// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
///
/// ### Why is this bad?
/// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
/// unintentional panics.
///
/// ### Example
/// ```rust
/// let time_passed = Instant::now() - Duration::from_secs(5);
/// ```
///
/// Use instead:
/// ```rust
/// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
/// ```
///
/// [`Duration`]: std::time::Duration
/// [`Instant::now()`]: std::time::Instant::now;
#[clippy::version = "1.65.0"]
pub UNCHECKED_DURATION_SUBTRACTION,
suspicious,
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
}
pub struct UncheckedDurationSubtraction {
msrv: Option<RustcVersion>,
}
impl UncheckedDurationSubtraction {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]);
impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) {
return;
}
if_chain! {
if let ExprKind::Binary(op, lhs, rhs) = expr.kind;
if let BinOpKind::Sub = op.node;
// get types of left and right side
if is_an_instant(cx, lhs);
if is_a_duration(cx, rhs);
then {
print_lint_and_sugg(cx, lhs, rhs, expr)
}
}
}
}
fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr);
match expr_ty.kind() {
rustc_middle::ty::Adt(def, _) => {
clippy_utils::match_def_path(cx, dbg!(def).did(), &clippy_utils::paths::INSTANT)
},
_ => false,
}
}
fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr);
ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration)
}
fn print_lint_and_sugg(cx: &LateContext<'_>, left_expr: &Expr<'_>, right_expr: &Expr<'_>, expr: &Expr<'_>) {
let mut applicability = Applicability::MachineApplicable;
let left_expr =
source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
let right_expr = source::snippet_with_applicability(
cx,
right_expr.span,
"std::time::Duration::from_secs(1)",
&mut applicability,
);
diagnostics::span_lint_and_sugg(
cx,
UNCHECKED_DURATION_SUBTRACTION,
expr.span,
"unchecked subtraction of a 'Duration' from an 'Instant'",
"try",
format!("{}.checked_sub({}).unwrap();", left_expr, right_expr),
applicability,
);
}

View File

@ -213,7 +213,7 @@ define_Conf! {
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE.
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION.
///
/// The minimum rust version that the project supports
(msrv: Option<String> = None),