-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Convert lints to use a trait-based infrastructure #14804
Conversation
cc me |
We're going to have more modules under lint, and the paths get unwieldy. We also plan to have lints run at multiple points in the compilation pipeline.
The immediate benefits are * moving the state specific to a single lint out of Context, and * replacing the soup of function calls in the Visitor impl with more structured control flow But this is also a step towards loadable lints.
Also change some code formatting. lint::builtin becomes a sibling of lint::context in order to ensure that lints implemented there use the same public API as lint plugins.
Re-pushed with a rebase and some doc fixes. |
Can you profile lint passes running on something like rustc or servo? There are many many more virtual calls than there were before, and I want to make sure that this doesn't regress too much. |
I'm not keen on this using Could there be a Theoretically this would allow for phased lints, by making it something like enum LintPhase { AstOnly, Resolved, TypeChecked }
trait LintPass {
fn initialise(phase: LintPhase, ...) -> Option<Self> and then lints that don't want to run for some particular phase would just return |
Sure, that's a good idea. |
@huonw: There's only one Basically we're just using This is done just so that the The reason for For a plugin lint, the plugin registrar creates a trait object and does initialization there. If we want that initialization to have access to the crate, say, then we can provide that through the I was thinking that phase would be an additional field in |
Thanks again for doing this by the way, this looks pretty awesome! |
It wasn't a very appropriate use of the trait. Instead, just enumerate unit structs and those with a `fn new()` separately.
Suggested viewing: git show -w
In preparation for the next commit.
The proper, more general solution is to skip a lint pass entirely when all the lints it could emit are Allow. That should probably be a ticket once this lands.
It's really too early to be thinking about this as a separate "public" API.
None of the builtin lints use this, and it's now available through the Context.
Thanks for the many helpful comments everyone! By my accounting, the remaining tasks are:
|
I do indeed!
My vote is for "not yet", but plausibly in the future.
Thanks again for this! |
I benchmarked this by building stage2 librustc, with optimization enabled, using a stage1 rustc built with optimization enabled. The time spent on lint checking increased from 0.677 s to 1.193 s, an increase of about 76%. However lint checking still takes only 11% as long as type checking, and 0.4% as long as the backend. Ratios for other crates like libsyntax are similar. So I think this is an acceptable regression. The other pass timings remained roughly the same, as expected. The code being compiled is also different between these two runs, but the amount of code hasn't changed substantially. This refactor also makes it much easier to skip lint passes that are set to |
What's the practical reason against it? The code is already written and it's not that ugly. The output lag is less than 1 second even on a huge crate, and Also the lint plugin RFC was accepted, so this is less of a speculative future thing. |
If we don't implement |
This is now #15024. I'm leaving the old branch alone to preserve discussion history. |
This is a rebase of #14804 with two new commits on top to implement and test lint plugins. r? @alexcrichton @huonw: Can you take a look at the new commits, and also weigh in about any issues from the old PR that you feel are still unresolved? I'm leaving the old branch alone to preserve discussion history.
This untangles each lint's code and state from the overall
Context
and itsVisitor
implementation. It also replaces theenum Lint
with an "extensible variant" based on the address of a staticLint
struct.Actually implementing lint plugins on top of this is only about 30 lines of code, but it's waiting on rust-lang/rfcs#89, so probably won't land for a few weeks.
I'd like to get the non-user-facing parts merged sooner, because it's a generally desirable cleanup, and it's a total pain to rebase across any other lint change.