Skip to content

Commit 26bc88d

Browse files
authored
Merge pull request #2839 from mikerite/duration_subsec_pr_2
Add duration_subsec lint
2 parents 8f0edba + 725e962 commit 26bc88d

File tree

6 files changed

+129
-0
lines changed

6 files changed

+129
-0
lines changed

clippy_lints/src/duration_subsec.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use rustc::hir::*;
2+
use rustc::lint::*;
3+
use syntax::codemap::Spanned;
4+
5+
use crate::consts::{constant, Constant};
6+
use crate::utils::paths;
7+
use crate::utils::{match_type, snippet, span_lint_and_sugg, walk_ptrs_ty};
8+
9+
/// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
10+
/// from other `Duration` methods.
11+
///
12+
/// **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or
13+
/// `Duration::subsec_millis()` than to calculate them.
14+
///
15+
/// **Known problems:** None.
16+
///
17+
/// **Example:**
18+
/// ```rust
19+
/// let dur = Duration::new(5, 0);
20+
/// let _micros = dur.subsec_nanos() / 1_000;
21+
/// let _millis = dur.subsec_nanos() / 1_000_000;
22+
/// ```
23+
declare_clippy_lint! {
24+
pub DURATION_SUBSEC,
25+
complexity,
26+
"checks for calculation of subsecond microseconds or milliseconds"
27+
}
28+
29+
#[derive(Copy, Clone)]
30+
pub struct DurationSubsec;
31+
32+
impl LintPass for DurationSubsec {
33+
fn get_lints(&self) -> LintArray {
34+
lint_array!(DURATION_SUBSEC)
35+
}
36+
}
37+
38+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DurationSubsec {
39+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
40+
if_chain! {
41+
if let ExprBinary(Spanned { node: BiDiv, .. }, ref left, ref right) = expr.node;
42+
if let ExprMethodCall(ref method_path, _ , ref args) = left.node;
43+
if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(&args[0])), &paths::DURATION);
44+
if let Some((Constant::Int(divisor), _)) = constant(cx, cx.tables, right);
45+
then {
46+
let suggested_fn = match (method_path.name.as_str().as_ref(), divisor) {
47+
("subsec_micros", 1_000) => "subsec_millis",
48+
("subsec_nanos", 1_000) => "subsec_micros",
49+
("subsec_nanos", 1_000_000) => "subsec_millis",
50+
_ => return,
51+
};
52+
span_lint_and_sugg(
53+
cx,
54+
DURATION_SUBSEC,
55+
expr.span,
56+
&format!("Calling `{}()` is more concise than this calculation", suggested_fn),
57+
"try",
58+
format!("{}.{}()", snippet(cx, args[0].span, "_"), suggested_fn),
59+
);
60+
}
61+
}
62+
}
63+
}

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ pub mod doc;
116116
pub mod double_comparison;
117117
pub mod double_parens;
118118
pub mod drop_forget_ref;
119+
pub mod duration_subsec;
119120
pub mod else_if_without_else;
120121
pub mod empty_enum;
121122
pub mod entry;
@@ -423,6 +424,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
423424
reg.register_late_lint_pass(box inherent_impl::Pass::default());
424425
reg.register_late_lint_pass(box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
425426
reg.register_late_lint_pass(box unwrap::Pass);
427+
reg.register_late_lint_pass(box duration_subsec::DurationSubsec);
426428

427429

428430
reg.register_lint_group("clippy_restriction", vec![
@@ -517,6 +519,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
517519
drop_forget_ref::DROP_REF,
518520
drop_forget_ref::FORGET_COPY,
519521
drop_forget_ref::FORGET_REF,
522+
duration_subsec::DURATION_SUBSEC,
520523
entry::MAP_ENTRY,
521524
enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
522525
enum_variants::ENUM_VARIANT_NAMES,
@@ -789,6 +792,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
789792
cyclomatic_complexity::CYCLOMATIC_COMPLEXITY,
790793
double_comparison::DOUBLE_COMPARISONS,
791794
double_parens::DOUBLE_PARENS,
795+
duration_subsec::DURATION_SUBSEC,
792796
eval_order_dependence::DIVERGING_SUB_EXPRESSION,
793797
eval_order_dependence::EVAL_ORDER_DEPENDENCE,
794798
explicit_write::EXPLICIT_WRITE,

clippy_lints/src/utils/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
2727
pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
2828
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
2929
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
30+
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
3031
pub const FMT_ARGUMENTS_NEWV1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
3132
pub const FMT_ARGUMENTS_NEWV1FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
3233
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];

tests/ui/duration_subsec.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![warn(duration_subsec)]
2+
3+
use std::time::Duration;
4+
5+
fn main() {
6+
let dur = Duration::new(5, 0);
7+
8+
let bad_millis_1 = dur.subsec_micros() / 1_000;
9+
let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
10+
let good_millis = dur.subsec_millis();
11+
assert_eq!(bad_millis_1, good_millis);
12+
assert_eq!(bad_millis_2, good_millis);
13+
14+
let bad_micros = dur.subsec_nanos() / 1_000;
15+
let good_micros = dur.subsec_micros();
16+
assert_eq!(bad_micros, good_micros);
17+
18+
// Handle refs
19+
let _ = (&dur).subsec_nanos() / 1_000;
20+
21+
// Handle constants
22+
const NANOS_IN_MICRO: u32 = 1_000;
23+
let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
24+
25+
// Other literals aren't linted
26+
let _ = dur.subsec_nanos() / 699;
27+
}

tests/ui/duration_subsec.stderr

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error: Calling `subsec_millis()` is more concise than this calculation
2+
--> $DIR/duration_subsec.rs:8:24
3+
|
4+
8 | let bad_millis_1 = dur.subsec_micros() / 1_000;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
6+
|
7+
= note: `-D duration-subsec` implied by `-D warnings`
8+
9+
error: Calling `subsec_millis()` is more concise than this calculation
10+
--> $DIR/duration_subsec.rs:9:24
11+
|
12+
9 | let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
14+
15+
error: Calling `subsec_micros()` is more concise than this calculation
16+
--> $DIR/duration_subsec.rs:14:22
17+
|
18+
14 | let bad_micros = dur.subsec_nanos() / 1_000;
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
20+
21+
error: Calling `subsec_micros()` is more concise than this calculation
22+
--> $DIR/duration_subsec.rs:19:13
23+
|
24+
19 | let _ = (&dur).subsec_nanos() / 1_000;
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()`
26+
27+
error: Calling `subsec_micros()` is more concise than this calculation
28+
--> $DIR/duration_subsec.rs:23:13
29+
|
30+
23 | let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
32+
33+
error: aborting due to 5 previous errors
34+

tests/ui/duration_subsec.stdout

Whitespace-only changes.

0 commit comments

Comments
 (0)