mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-19 11:12:43 +00:00
feat: add and implement unchecked_duration_subtraction lint
This commit is contained in:
parent
432baf7026
commit
708c2d95c1
@ -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
|
||||
|
@ -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`
|
||||
}
|
||||
|
||||
|
107
clippy_lints/src/unchecked_duration_subtraction.rs
Normal file
107
clippy_lints/src/unchecked_duration_subtraction.rs
Normal 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,
|
||||
);
|
||||
}
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user