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

#9231 suggest TryFrom when truncation possible #9664

Closed
wants to merge 2 commits into from

Conversation

navh
Copy link
Contributor

@navh navh commented Oct 17, 2022

changelog: Sugg: [cast_possible_truncation]: Now suggests try_from

fixes #9231

@rust-highfive
Copy link

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @xFrednet (or someone else) soon.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Oct 17, 2022
@kraktus
Copy link
Contributor

kraktus commented Oct 17, 2022

The truncation can be intentional and then suggesting tryFrom would change the behaviour of the code, I don't think the suggestion can be machine applicable.

expr.span,
&msg,
"avoid silent truncation by using",
format!("{cast_to}::try_from({name_of_cast_from}).unwrap()"),
Copy link
Contributor

Choose a reason for hiding this comment

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

I think using using expect here and hinting developers to explain why it shouldn't truncate would be even better

Suggested change
format!("{cast_to}::try_from({name_of_cast_from}).unwrap()"),
format!("{cast_to}::try_from({name_of_cast_from}).expect("truncation should be impossible because ...)"),

Copy link
Contributor Author

@navh navh Oct 17, 2022

Choose a reason for hiding this comment

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

I've made this change and changed MachineApplicable to HasPlaceholders, is this the best way to classify this?

I tried to come up with some sort of {cast_to}::try_from({} & {cast_to}::MAX as {cast_from}).unwrap() machine applicable suggestion, but they all bury the intention of the lint.

Copy link
Contributor

Choose a reason for hiding this comment

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

HasPlaceholders is fine here 👍 Another option now that I think about it would be to make the whole expect reason a placeholder so that the user wouldn't be able to copy-paste the offered suggestion without having to think about the issue before getting a compilable version of their program. What do you think @xFrednet?

format!("{cast_to}::try_from({name_of_cast_from}).expect(..)

Copy link
Member

@xFrednet xFrednet Oct 19, 2022

Choose a reason for hiding this comment

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

Thank you for the review and feedback @kraktus, it's highly appreciated!

Using try_from() changes the semantics of the code. In most cases, users would most likely prefer the value to be truncated over the code panicking in production. This also misses cases, where this behavior might be intentional. Though, looking at the lint description, it's not ideal to emit a lint without any suggestion.

The idea of using try_from is good and should be included in the lint message. The applicability of HasPlaceholders seamed a bit high to me at first, but good at a second thought.

That being said, I would suggest a different lint message. Something like this:

error: casting `f64` to `isize` may truncate the value
  --> $DIR/cast.rs:29:5
   |
LL |     1f64 as isize;
   |
help: if this is intensional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
help: ... or use `try_from` and handle the error accordingly
   |
LL | isize::try_from(1f64)
   |

Note that this changes the semantics even more, as we don't instruct the user to use .expect("..") or .unwrap(). For this, I would make the applicability unspecified.

What are your thoughts about this? 🙃


You can create more complex messages with: span_lint_and_then.

Here is a mockup how the code could look:
span_lint_and_then(
    cx,
    <LINT>,
    <span>,
    <msg>,
    |diag| {
        diag.help(
            "if this is intensional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...",
        );
        diag.span_suggestion_with_style(
            <span>,
            "... or use `try_from` and handle the error accordingly",
            suggestion,
            Applicability::Unspecified,
            // always show the suggestion in a separate line
            SuggestionStyle::ShowAlways,
        );
    },
);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm having trouble getting the two "help" messages to align to the left.

This makes it awkward to read. Note appearing in the middle for some lints also breaks it up in an awkward way.

Where can I go to learn more about formatting this output? Or is this correct?

error: casting `f32` to `i32` may truncate the value
  --> $DIR/cast.rs:24:5
   |
LL |     1f32 as i32;
   |     ^^^^^^^^^^^
   |
   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
   = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
help: ... or use `try_from` and handle the error accordingly
   |
LL |     i32::try_from(1f32);
   |     ~~~~~~~~~~~~~~~~~~~

Copy link
Member

Choose a reason for hiding this comment

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

This is correct, i mixed up the formatting. You could try add the #[allow] attribute as another code suggestion, but I think this is better. Only thing is the note in the middle which we sadly can influence directly.

There is sadly no detailed documentation for the formatting.

Could you also update the lint description to mention these two solutions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I put 'why is this bad' back to how it was, and appended a bit to the 'what it does' to reflect both the ignore and try_from suggestions.

@xFrednet
Copy link
Member

Hey, I'll try to take a look at this soon, by mid next week at the latest.

A general FYI, Clippy has a no merge commit policy (with a few exceptions), PR branches should be rebased instead. Here is an info link on how to resolve merge conflicts instead. Sometimes it can be simpler to squash all commits into one. I'm mentioning this, as your branch tries to add a bunch of external and merge commits. We can also take a look at this after the next review. You're welcome to reach out if you need help with that :)

@navh navh force-pushed the usize_to_u64 branch 2 times, most recently from d054509 to be27fa7 Compare October 21, 2022 21:04
@navh
Copy link
Contributor Author

navh commented Oct 21, 2022

Sometimes it can be simpler to squash all commits into one.

I should have taken this advice, instead I've force pushed a ton.... Documentation changes got lost in the crossfire, so I've re-done it, hopefully this works.

@bors
Copy link
Contributor

bors commented Oct 23, 2022

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

Copy link
Member

@xFrednet xFrednet left a comment

Choose a reason for hiding this comment

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

This version already looks good to me. Two small adjustments and then we can have this merged :). Thank you for the quick updates.

@@ -80,7 +80,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for casts between numerical types that may
/// truncate large values. This is expected behavior, so the cast is `Allow` by
/// default.
/// default. It suggests user either explicitly ignore the lint,
/// or use `try_from()` and handle the truncation, default, or panic explicitly.
Copy link
Member

Choose a reason for hiding this comment

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

Could you also add a Use instead: block below, like this:

Use instead:
```
fn as_u8(x: u64) -> u8 {
    if let Ok(x) = u8::try_from(x) {
        x
    } else {
        todo!();
    }
}
// Or
fn as_u16(x: u64) -> u16 {
    #[allow(clippy::cast_possible_truncation)]
    x as u16
}
```

Comment on lines +164 to +165
let snippet = snippet(cx, expr.span, "x");
let name_of_cast_from = snippet.split(" as").next().unwrap_or("x");
Copy link
Member

Choose a reason for hiding this comment

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

Clippy usually uses .. as placeholders. Could you adjust this?

Suggested change
let snippet = snippet(cx, expr.span, "x");
let name_of_cast_from = snippet.split(" as").next().unwrap_or("x");
let snippet = snippet(cx, expr.span, "..");
let name_of_cast_from = snippet.split(" as").next().unwrap_or("..");

This should hopefully never come up either way :)

@xFrednet
Copy link
Member

xFrednet commented Nov 5, 2022

Hey @navh, this is a ping from triage. Are you stuck on anything, that I can help with?

@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 Nov 5, 2022
@xFrednet
Copy link
Member

xFrednet commented Dec 6, 2022

I'm closing this in favor of #10038 I tried to keep the original commiter from this PR :)

@xFrednet xFrednet closed this Dec 6, 2022
bors added a commit that referenced this pull request Jan 14, 2023
`cast_possible_truncation` Suggest TryFrom when truncation possible

This fixes the last issues from #9664 as the author seems to be inactive. The PR author was sadly kept during the rebase, due to the conflict resolution.

IDK if it's worth it do to a full review, I only added the last commit, everything else remained the same, besides a rebase.

---

changelog: Sugg: [`cast_possible_truncation`]: Now suggests using `try_from` or allowing the lint
[#10038](#10038)
<!-- changelog_checked -->

closes: #9231
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.

Have a lint against usize-to-u64 casts (or, against *all* integer casts)
6 participants