-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Conversation
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. |
The truncation can be intentional and then suggesting |
expr.span, | ||
&msg, | ||
"avoid silent truncation by using", | ||
format!("{cast_to}::try_from({name_of_cast_from}).unwrap()"), |
There was a problem hiding this comment.
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
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 ...)"), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(..)
There was a problem hiding this comment.
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,
);
},
);
There was a problem hiding this comment.
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);
| ~~~~~~~~~~~~~~~~~~~
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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 :) |
d054509
to
be27fa7
Compare
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. |
☔ The latest upstream changes (presumably #9541) made this pull request unmergeable. Please resolve the merge conflicts. |
There was a problem hiding this 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. |
There was a problem hiding this comment.
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
}
```
let snippet = snippet(cx, expr.span, "x"); | ||
let name_of_cast_from = snippet.split(" as").next().unwrap_or("x"); |
There was a problem hiding this comment.
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?
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 :)
I'm closing this in favor of #10038 I tried to keep the original commiter from this PR :) |
`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
changelog: Sugg: [
cast_possible_truncation
]: Now suggeststry_from
fixes #9231