-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix(use_self): don't early-return if the outer type has no lifetimes #15611
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
base: master
Are you sure you want to change the base?
Conversation
r? @Alexendoo rustbot has assigned @Alexendoo. Use |
r? clippy |
Lintcheck changes for e7c91ab
This comment will be updated if you push new changes |
Although now that I think of it, maybe checking only the top-level type was the point of @Ethiraric, I see that you were the one who added this function back in #12386 -- do you maybe remember the motivation behind it? Looking at lintcheck, this PR does add FNs in real-life code, which is of course unfortunate, though it's all elided-lifetimes-related, which that PR explicitly left out as future work -- I guess it's FNs are preferable to positives that only happen to be true positives due to a bug... |
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.
"misc" is not a very descriptive commit name
That's the solution I came up with as a way to avoid having multiple Please do tell me how you would like to see those misc changes committed (maybe as a separate PR with multiple commits?), and I'll happily follow that. |
I unfortunately don't recall the exact reason why. I was discovering the codebase at that time. I've had a look at #12386 and #12381 (the associated issue) but can't figure out more context out of it.
This looks like an oversight from me, more than an informed decision. |
Thank you for the quick response @Ethiraric:)
In that case I think I might as well remove that method^^ |
Whether an oversight or not, as long as the tests pass and a rationale for removing it is established, it's all good. I saw the example in issue 13277 and the contents of my (former) function don't seem at all to be able to handle it :) |
I'm on vacation right now (this assignment reminded me to actually tell triagebot that). So re-assigning to Samuel, as he already took a look. r? samueltardieu |
I feel you. With "misc", you cannot tell whether the commit changes things or not. If the commit does some refactoring and cleanups, "Minor cleanup" would be appropriate, and if some code got refactored into a utility function for example you can say that in the longer description. If I see a commit titled "Minor cleanup", I would expect any behavior difference between the state before the commit and the state after the commit to be an unintended change, thus a bug. This facilitates a commit-by-commit review. Also, when chasing regression between releases, we might need to look at commits that touch some file. Bisecting is not always easy to do, as some versions don't compile (this should never happen in my opinion, every commit should pass the tests, but that's another story). Being able to skip "Minor cleanup", or "Refactoring" commits in the first pass makes things easier. So thank you for separating iso-functionality changes into separate commits (I like that!), but we have to find a middle ground between commit spam in PRs and non-descriptive messages. Note that this depends on the project: on Linux, QEMU, or jj, I would make a lot of smaller commits as you did, because this is how their review process works. In Rust or Clippy, people tend to not like PRs with lots of commits inside, although very small commits are very often accepted in isolation. I understand things would be easier if we had PRs stack as independent cleanup PRs could be depended on by a more concrete PR, and each cleanup PR could be reviewed and accepted very quickly. Unfortunately, GitHub PR model makes this difficult… |
Thank you for the empathy:) I agree that "misc" was too short to be helpful -- "minor cleanup" plus an optional description of larger refactorings does sound like a better middle-ground.
Very much agree -- I sometimes go as far as
That is somewhat surprising to me, because Rust's syntax can be quite diff-unfriendly sometimes.. I guess using word-diffs and programs like difftastic could help a bit. Again, thank you for taking the time to write this out -- it helps a lot to know what the person on the other side has on their mind when looking at my PR:) |
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.
In addition to the "misc" commit name, some small nits.
(GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, | ||
(GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_type_and_consts(type_a, type_b), | ||
_ => true, | ||
}) |
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 know this predates your patch, but shouldn't this function name contain _modulo_regions
, and document that lifetime mismatches don't cause the function to return false
?
I'd be curious to know whether this makes sense to ignore the lifetimes in all use cases of this function though, but at least this would make the intention clearer.
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.
but shouldn't this function name contain
_modulo_regions
, and document that lifetime mismatches don't cause the function to returnfalse
As #15610 just pointed out, a lot of things in Clippy tend to ignore lifetimes it seems..
I guess lifetimes being ignored is implied by contrast ("only takes into account the type and const generics, therefore ignores lifetimes"), but a more explicit name could not hurt. I think same_types_modulo_regions
is a good option.
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 added a few "doctests" for some extra clarity (I often wish more util functions included these, to help understand what the function does at a glance) -- let me know what you think
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.
You mean "examples", not "doctests", right?
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.
Well I avoided calling them "doctests" because the main thing about doctests is that they're, well, tests, that you can run^^ I really wish there were actual doctests on these things, but that would require somehow getting HIR/AST nodes to pass into the functions, which I think would require to run the entire compiler pipeline for each test? That would be a bit wasteful of course
EDIT: Ah, sorry, misread your comment. Yes, that's why put "doctests" in quotes -- they aren't actual doctests after all. The rest of my comment still holds though^^
tests/ui/use_self.rs
Outdated
@@ -531,7 +531,7 @@ mod issue7206 { | |||
impl<'a> S2<S<'a>> { | |||
fn new_again() -> Self { | |||
S2::new() | |||
//~^ use_self | |||
// FIXME: this^ should 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.
Just to make sure:
- If this linted before, maybe add
Broken by PR #15611
and open an issue when this PR is merged (or, better, fix it if it can be done easily) - If this doesn't lint even before this PR, can you open an issue and reference it in the comment?
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.
It's a bit complicated:
S2::new()
actually contains an erased1 lifetime (the '_
in S2::<S<'_>>::new()
), and when #12386 first added the handling of lifetimes, it explicitly left out erased lifetimes as future work.
The only reason this has worked before this PR was that the !has_lifetime
check false-positively fired on S2<S<'a>>
, because it only looked for generic lifetime params on S2
, forgetting to descend into S
-- and so the lint fired before having the chance to check same_lifetimes
. And now that I first made has_lifetime
descend into inner types, and then removed it, same_lifetimes
stops the lint from firing, because it doesn't handle the erased lifetime.
So if I were to open an issue, it would pretty much amount to "C-improvement
: use_self
doesn't handle erased lifetimes" -- which I guess is fair enough. Fixing that issue would be non-trivial (that is actually what I tried to do for a while before giving up and opening this PR, with the breakage included), and would need to wait for the end of the feature freeze anyway.
Footnotes
-
"erased" might be a synonym for "elided", but I'm not sure, so I'll stick to the former ↩
98e7329
to
facb442
Compare
/// | ||
/// `True` for: | ||
/// - `Option<u32>` and `Option<u32>` | ||
/// - `[u8; N]` and `[u8; M]`, if `N=M` |
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.
/// - `[u8; N]` and `[u8; M]`, if `N=M` | |
/// - `[u8; N]` and `[u8; M]`, if `same_type_modulo_regions(N, M)` holds |
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.
Hm, not really -- N
and M
would be Const
s, not Ty
s. I could add an example like
Option<T>
andOption<U>
, ifsame_type_modulo_regions(T, U)
holds
to illustrate that case though
clippy_utils/src/ty/mod.rs
Outdated
/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores | ||
/// lifetimes. | ||
/// | ||
/// `True` for: |
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.
It would be clearer to indicate that those are examples, not the only cases handled:
/// `True` for: | |
/// For example, the function would return `true` for |
also the constant is true
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.
also the constant is true
I capitalized it because it was kind of the first word in the sentence, but yeah you're right.
clippy_utils/src/ty/mod.rs
Outdated
/// - `[u8; N]` and `[u8; M]`, if `N=M` | ||
/// - `&'a str` and `&'b str` | ||
/// | ||
/// `False` for: |
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.
/// `False` for: | |
/// and `false` for: |
(GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, | ||
(GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_type_and_consts(type_a, type_b), | ||
_ => true, | ||
}) |
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.
You mean "examples", not "doctests", right?
Notably, rename `same_type_and_consts` to `same_type_modulo_regions` to be more explicit about the function ignoring lifetimes
This unfortunately breaks another test case -- it had only worked because when we checked `!has_lifetime` on `S2<S<'a>>`, we only looked for generic lifetime params on `S2`, forgetting to descend into `S`. One thing that makes this a bit less sad is that this particular test case doesn't come from a reported issue, but rather was added as a miscellaneous check during a kind of unrelated PR.
It's now redundant to `same_lifetimes`: if `ty` and `impl_ty` are the same type, and they both have no lifetimes, `same_lifetimes` will return `true` already
facb442
to
e7c91ab
Compare
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
Fixes #13277
Unfortunately breaks another case, which has only been working thanks to the now fixed bug -- see last commit.
changelog: [
use_self
]: don't early-return if the outer type has no lifetimes