Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lint which checks that duration conversion aren't losing precision #12539

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

declanvk
Copy link

Description
Add a lint which checks for a binop or cast where the expression is converting a Duration to a floating point number, and losing precision along the way.

The lint won't fire for most cases involving as_nanos() since converting that to floating point already gives the max precision.

The lint is also restricted to an MSRV of 1.38.0, since that is when the as_secs_{f32,f64}() methods were stabilized.

Motivation
This change is motivated by a rust stdlib ACP which proposed as_millis_{f64,f32}. As part of that I did some code searches on github (see ACP for links) that showed a lot of code which converted Duration values to floating point using methods other than as_secs_{f32,64}(), and were losing precision because of that.

This lint seems like a good way to raise awareness and prompt using the existing methods.

Testing Done
Added UI tests, ran cargo test, followed the Clippy manual


changelog: [duration_to_float_precision_loss]: Add new lint which checks for precision loss in duration to float conversions

@rustbot
Copy link
Collaborator

rustbot commented Mar 23, 2024

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @blyxyas (or someone else) some time within the next two weeks.

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Mar 23, 2024
@samueltardieu
Copy link
Contributor

I've ran lintcheck with your patch applied on the top 500 crates:

bindgen-0.69.4/time.rs:38:24 clippy::duration_to_float_precision_loss "calling `as_secs_f64() * 1e3` is more precise than this calculation"
indicatif-0.17.8/src/state.rs:655:5 clippy::duration_to_float_precision_loss "calling `as_secs_f64()` is more precise than this calculation"
serde_with-3.7.0/src/utils.rs:117:5 clippy::duration_to_float_precision_loss "calling `as_secs_f64()` is more precise than this calculation"
serde_with-3.7.0/src/utils/duration.rs:181:42 clippy::duration_to_float_precision_loss "calling `as_secs_f64()` is more precise than this calculation"

So it looks like this lint could be useful.

@declanvk
Copy link
Author

bindgen-0.69.4/time.rs:38:24:

let time = (elapsed.as_secs() as f64) * 1e3 +
    (elapsed.subsec_nanos() as f64) / 1e6;

indicatif-0.17.8/src/state.rs:655:

d.as_secs() as f64 + f64::from(d.subsec_nanos()) / 1_000_000_000f64

serde_with-3.7.0/src/utils.rs:117:5

(dur.as_secs() as f64) + (dur.subsec_nanos() as f64) / (NANOS_PER_SEC as f64)

serde_with-3.7.0/src/utils/duration.rs:181:42

let mut secs = source.sign.apply(source.duration.as_secs() as f64);

The source code of as_secs_f64 is:

pub const fn as_secs_f64(&self) -> f64 {
    (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64)
}

In order, the code from the linted crates appears to be calculating:

  1. the number of milliseconds as a float with nanosecond precision
  2. the number of seconds as a float with nanosecond precision
  3. the number of seconds as a float with nanosecond precision
  4. the number of seconds with no added precision

I think the lint application is probably okay advice for 2-4, but the advice it will give in the 1st case may be a bit confusing.

Maybe it would be worthwhile to try to recognize the case where someone is doing the as_secs_f64 calculation manually? Also might be worthwhile to support {f32,f64}::from(number) instead of just the _ as {f32,f64} cases.

@bors
Copy link
Contributor

bors commented Mar 30, 2024

☔ The latest upstream changes (presumably #12312) made this pull request unmergeable. Please resolve the merge conflicts.

@blyxyas
Copy link
Member

blyxyas commented Apr 1, 2024

Honestly @declanvk, this is some of the best-written code, comments and PR description I've ever read. Congrats on making the code so understandable!

Normally, 500+ lines diffs are quite painful to read, but this one is quite nice ❤️

Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great for a first iteration. Meow meow 😸

clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
tests/ui/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
@declanvk
Copy link
Author

declanvk commented Apr 1, 2024

Thank you @blyxyas for reviewing! I'll address your comments in my next attempt.

I wanted to get your opinion on the lint category versus the number of false positives. I placed the lint in the nursery to start, but I wasn't sure if it should move elsewhere? Or if that should be a later PR after the lint has baked a while.

Also, based on the data @samueltardieu gathered (thank you!), are you worried about any of the cases from #12539 (comment)? Arguable, cases 1-3 have slightly off suggestions from the lint.

@declanvk declanvk force-pushed the duration-to-float branch 2 times, most recently from 0749e19 to 056ed4a Compare April 3, 2024 04:36
@declanvk
Copy link
Author

declanvk commented Apr 3, 2024

I pushed a new revision addressing 3/4 comments, let me know your thoughts on the last comment I left unresolved. Thanks again!

@declanvk declanvk requested a review from blyxyas April 22, 2024 17:39
Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay, some nits, some comments. Still very good for a first contribution. I really like your use of useful comments.

clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
tests/ui/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
@blyxyas
Copy link
Member

blyxyas commented Apr 27, 2024

I forgot to ask, are you planning to implement #12539 (comment) or is this good enough? This is up to you.

@declanvk
Copy link
Author

Oh sorry! I added the commit just because it was the easiest way to apply your suggestions, I'm still planning to work on the other comments/open that issue possibly

@bors
Copy link
Contributor

bors commented May 12, 2024

☔ The latest upstream changes (presumably #12107) made this pull request unmergeable. Please resolve the merge conflicts.

@declanvk
Copy link
Author

declanvk commented Jun 2, 2024

I added the rest of the requested changes to the commit, and I'm planning to cut that issue about recognizing (duration.as_secs() * 1000) as f64 as a separate issue once this merged.

Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nit, and this should be ready! (We don't want to lint proc macros)

clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
declanvk and others added 2 commits June 3, 2024 15:34
**Description**
Add a lint which checks for a binop or cast where the expression
is converting a Duration to a floating point number, and losing
precision along the way.

The lint won't fire for most cases involving `as_nanos()` since
converting that to floating point already gives the max precision.

The lint is also restricted to an MSRV of 1.38.0, since that is
when the `as_secs_{f32,f64}()` methods were stabilized.

**Motivation**
This change is motivated by [a rust stdlib ACP which proposed
`as_millis_{f64,f32}`][ACP-link]. As part of that I did some code
searches on github (see ACP for links) that showed a lot of code
which converted Duration values to floating point using methods other
than `as_secs_{f32,64}()`, and were losing precision because of that.

This lint seems like a good way to raise awareness and prompt
using the existing methods.

[ACP-link]: rust-lang/libs-team#349

**Testing Done**
Added UI tests, ran `cargo test`, followed the Clippy manual
 - I applied about half of these from the UI
 - Made more parts of the example code visible
 - Added test on duration ref
 - Rebased over conflict
 - Moved to the `style` lint group

Co-authored-by: Alejandra González <[email protected]>
Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a version update, and this should be ready! I've already opened the FCP thread on our Zulip :)

If everything goes as planned, this should be merged in about a week! Thanks for your contribution, and welcome to the project! ❤️

https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy/topic/FCP.20.60duration_to_float_precision_loss.60

clippy_lints/src/duration_to_float_precision_loss.rs Outdated Show resolved Hide resolved
Co-authored-by: Alejandra González <[email protected]>
@declanvk
Copy link
Author

declanvk commented Jun 4, 2024

Thanks for all your work in this code review! And sorry about the mini-hiatus I took in the middle 😁 . I'm looking forward to merging this in!

I'm still planning to create that followup issue about the alternate forms of duration conversion too.

@declanvk
Copy link
Author

declanvk commented Jun 4, 2024

Also, should I respond to questions/comments on the Zulip thread? Or is that mostly for core contributors?

}

fn emit_lint(&self, cx: &LateContext<'_>) {
let mut applicability = Applicability::MachineApplicable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to use MaybeIncorrect since there's a behaviour change. A note making it clear that the suggestion would include fractional e.g. milliseconds would also be good

@blyxyas
Copy link
Member

blyxyas commented Jun 5, 2024

Also, should I respond to questions/comments on the Zulip thread? Or is that mostly for core contributors?

Rust's Zulip is open to anyone and everyone, in fact, it's better that you yourself (as the PR author) respond these questions.
Meow! =^w^=

@bors
Copy link
Contributor

bors commented Jun 15, 2024

☔ The latest upstream changes (presumably #12937) made this pull request unmergeable. Please resolve the merge conflicts.

@xFrednet
Copy link
Member

xFrednet commented Jul 4, 2024

Hey @declanvk , this is a ping from triage, since there hasn't been any activity in some time. Are you still planning to continue this implementation?

If you have any questions, you're always welcome to ask them in this PR or on Zulip.

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Jul 4, 2024
@declanvk
Copy link
Author

declanvk commented Jul 4, 2024

Hey @xFrednet, I am planning to continue, but it might take me a second to get back to this. Thanks for checking in!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants