refactor: lint man. instant elapsed and unch. dur. subtr. in single pass
This commit is contained in:
parent
b485832b16
commit
2f2eb2e4ba
@ -1,17 +1,48 @@
|
|||||||
use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty};
|
use clippy_utils::{
|
||||||
|
diagnostics::{self, span_lint_and_sugg},
|
||||||
|
meets_msrv, msrvs, source, ty,
|
||||||
|
};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::*;
|
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
use rustc_semver::RustcVersion;
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::{source_map::Spanned, sym};
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Lints subtraction between `Instant::now()` and another `Instant`.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
|
||||||
|
/// as `Instant` subtraction saturates.
|
||||||
|
///
|
||||||
|
/// `prev_instant.elapsed()` also more clearly signals intention.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// use std::time::Instant;
|
||||||
|
/// let prev_instant = Instant::now();
|
||||||
|
/// let duration = Instant::now() - prev_instant;
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// use std::time::Instant;
|
||||||
|
/// let prev_instant = Instant::now();
|
||||||
|
/// let duration = prev_instant.elapsed();
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.64.0"]
|
||||||
|
pub MANUAL_INSTANT_ELAPSED,
|
||||||
|
pedantic,
|
||||||
|
"subtraction between `Instant::now()` and previous `Instant`"
|
||||||
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
|
/// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
|
/// Unchecked subtraction could cause underflow on certain platforms, leading to
|
||||||
/// unintentional panics.
|
/// unintentional panics.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
@ -32,21 +63,39 @@
|
|||||||
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
|
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UncheckedDurationSubtraction {
|
pub struct InstantSubtraction {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Option<RustcVersion>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UncheckedDurationSubtraction {
|
impl InstantSubtraction {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]);
|
impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]);
|
||||||
|
|
||||||
|
impl LateLintPass<'_> for InstantSubtraction {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||||
|
if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
|
||||||
|
&& check_instant_now_call(cx, lhs)
|
||||||
|
&& let ty_resolved = cx.typeck_results().expr_ty(rhs)
|
||||||
|
&& let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
|
||||||
|
&& clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
|
||||||
|
&& let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
|
||||||
|
{
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
MANUAL_INSTANT_ELAPSED,
|
||||||
|
expr.span,
|
||||||
|
"manual implementation of `Instant::elapsed`",
|
||||||
|
"try",
|
||||||
|
format!("{}.elapsed()", sugg.maybe_par()),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -67,6 +116,17 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
|
||||||
|
if let ExprKind::Call(fn_expr, []) = expr_block.kind
|
||||||
|
&& let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
|
||||||
|
&& clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||||
|
|
@ -151,6 +151,7 @@
|
|||||||
mod inherent_to_string;
|
mod inherent_to_string;
|
||||||
mod init_numbered_fields;
|
mod init_numbered_fields;
|
||||||
mod inline_fn_without_body;
|
mod inline_fn_without_body;
|
||||||
|
mod instant_subtraction;
|
||||||
mod int_plus_one;
|
mod int_plus_one;
|
||||||
mod invalid_upcast_comparisons;
|
mod invalid_upcast_comparisons;
|
||||||
mod invalid_utf8_in_unchecked;
|
mod invalid_utf8_in_unchecked;
|
||||||
@ -172,7 +173,6 @@
|
|||||||
mod manual_async_fn;
|
mod manual_async_fn;
|
||||||
mod manual_bits;
|
mod manual_bits;
|
||||||
mod manual_clamp;
|
mod manual_clamp;
|
||||||
mod manual_instant_elapsed;
|
|
||||||
mod manual_is_ascii_check;
|
mod manual_is_ascii_check;
|
||||||
mod manual_let_else;
|
mod manual_let_else;
|
||||||
mod manual_non_exhaustive;
|
mod manual_non_exhaustive;
|
||||||
@ -279,7 +279,6 @@
|
|||||||
mod trait_bounds;
|
mod trait_bounds;
|
||||||
mod transmute;
|
mod transmute;
|
||||||
mod types;
|
mod types;
|
||||||
mod unchecked_duration_subtraction;
|
|
||||||
mod undocumented_unsafe_blocks;
|
mod undocumented_unsafe_blocks;
|
||||||
mod unicode;
|
mod unicode;
|
||||||
mod uninit_vec;
|
mod uninit_vec;
|
||||||
@ -908,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
|
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
|
||||||
store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
|
store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
|
||||||
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
|
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
|
||||||
store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed));
|
store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv)));
|
||||||
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
|
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
|
||||||
store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
|
store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
|
||||||
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
|
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
|
||||||
use rustc_errors::Applicability;
|
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|
||||||
use rustc_span::source_map::Spanned;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
|
||||||
/// ### What it does
|
|
||||||
/// Lints subtraction between `Instant::now()` and another `Instant`.
|
|
||||||
///
|
|
||||||
/// ### Why is this bad?
|
|
||||||
/// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
|
|
||||||
/// as `Instant` subtraction saturates.
|
|
||||||
///
|
|
||||||
/// `prev_instant.elapsed()` also more clearly signals intention.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```rust
|
|
||||||
/// use std::time::Instant;
|
|
||||||
/// let prev_instant = Instant::now();
|
|
||||||
/// let duration = Instant::now() - prev_instant;
|
|
||||||
/// ```
|
|
||||||
/// Use instead:
|
|
||||||
/// ```rust
|
|
||||||
/// use std::time::Instant;
|
|
||||||
/// let prev_instant = Instant::now();
|
|
||||||
/// let duration = prev_instant.elapsed();
|
|
||||||
/// ```
|
|
||||||
#[clippy::version = "1.65.0"]
|
|
||||||
pub MANUAL_INSTANT_ELAPSED,
|
|
||||||
pedantic,
|
|
||||||
"subtraction between `Instant::now()` and previous `Instant`"
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]);
|
|
||||||
|
|
||||||
impl LateLintPass<'_> for ManualInstantElapsed {
|
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
|
||||||
if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
|
|
||||||
&& check_instant_now_call(cx, lhs)
|
|
||||||
&& let ty_resolved = cx.typeck_results().expr_ty(rhs)
|
|
||||||
&& let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
|
|
||||||
&& clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
|
|
||||||
&& let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
|
|
||||||
{
|
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
MANUAL_INSTANT_ELAPSED,
|
|
||||||
expr.span,
|
|
||||||
"manual implementation of `Instant::elapsed`",
|
|
||||||
"try",
|
|
||||||
format!("{}.elapsed()", sugg.maybe_par()),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
|
|
||||||
if let ExprKind::Call(fn_expr, []) = expr_block.kind
|
|
||||||
&& let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
|
|
||||||
&& clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
|
|
||||||
{
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user