-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add lint for reads and writes that depend on evaluation order #1158
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
Conversation
let a = { let mut x = 1; x = 2; 1} + x; | ||
|
||
// No warning if we don't read the variable... | ||
x = { x = 20; 2 }; |
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.
we should either fix rustc's "unused_assignments" lint or produce our own lint for this case.
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 easy if you just want to detect an assignments to x
whose RHS also assigns to x
; the lint would just need to put here. I can add that if you want, but I don't know what to call it.
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.
no need to do anything... rustc warns: https://is.gd/Gxtp1U
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 not for x = { x = 5; 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.
that's because it's not an unused assignment ;)
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.
x = (x = 5, x + 1).1;
is the more interesting case, since blocks have a controlled order of execution.
(Ah, sorry, those aren't outdated, I just amended because I missed some spaces in the test file). |
lgtm
we'll just open an issue :) |
Super nice lint! However, does this have a measurable impact on execution time? Walking up the tree on every assignment seems expensive. It's possible to make the same set of checks cheaper with a top-down approach -- using the _post methods on the lint pass, you can keep track of whether or not you're directly in an item scope and ignore those assignments (or make the whole check top-down, but this is more fiddly) |
I was worried about that too, but I can't really detect a difference with I'm very interested in making it faster though. Could you elaborate on how the top-down approach would work? |
Not So I haven't thought about the top-down approach completely yet 😄 A simple optimization you can do is set a flag when your block is directly within a function/impl item. You'll need to use A better optimization is to track which blocks are getting assigned to things, and set a flag for the duration of these blocks. This might get us sufficient optimization. Thinking more about it, getting it completely top-down is pretty icky and probably defeats the purpose since it would be just as expensive. |
Furthermore, this lint isn't that inefficient since it's only walking up a few nodes for each assignment. Not too bad, and since the node types are encoded in the node tree, it's just one cache miss per parent access (at max). Shouldn't cause a big issue. |
Looking at the
I'm not sure. It doesn't have to involve a block being assigned to something at all. The worst case is probably something like a deeply nested, very wide (ie. lots of args in every call) set of calls where the assignment happens on the deepest level and no read is found, so the lint doesn't trigger. |
I mean, a block being used within an op, assignment or otherwise. The time difference isn't that bad -- looks like the mean would be pretty small. It's okay to have a few slow lints. But optimizations would be nice to have 😄 |
Fixes #277. Well, for simple cases anyway.
It adds a late lint pass that looks for assignments. When it finds one, it walks up the AST from it and for every ancestor expression that depends on evaluation order (calls, binops, etc.) it descends all its children looking for reads to the same variable.
There are some limitations mentioned in the test file, but the main one is that it only works when the l-value is a simple variable name (
x
).