-
-
Notifications
You must be signed in to change notification settings - Fork 78
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
Discussion: ways of implementing autofix and issues #14
Comments
Is there a way to create a |
Not without manually adding tokens, no, but replace_with is more or less O(1) |
I think we can get away with an amazing UI with a fixer, we could leverage syntax nodes and the new |
That would work too. |
I looked at eslint's approach, it seems that they just do not apply fixes for a rule if the fix collides with another, i think this is fine for now, we should maybe issue an internal error if the fix spans overlap. |
What if we re run one of the fixes with the new fixed content if both fixes collide? Edit: that's probably going to be harder to implement. |
That would be arbitrary selection of which rule is "right", which i would rather stay away from. Edit: misunderstood what you meant, but It could also be exponentially complex and could likely throw the linter into tons of reruns which would be very expensive |
Yea that's not ideal. |
Eslint's approach seems to work well enough, they collect fixes and apply them sequentially, then if the next fix's range overlaps they don't apply the fix and just skip to the next iteration. I think this is good and we should try doing this. I would like to implement autofix for v0.2 once #16 has landed, since incremental reparsing will be a big part of the fix iterations. |
I would also like to eventually add SSR (structural search and replace), SSR is a tool for search and replace which operates directly on the syntax tree, for example: { get $name:name() $body } -> function $name () $body it could also be extended to match specific nodes like bin_expr, expr, etc. The premise of how it works is really simple and rslint's syntax makes it really easy (see rust analyzer's implementation). there are a few distinct steps: 1: template splitting, producing 2: lexing, the templates are lexed to identify the patterns in the text 3: replacement, 4: parsing, the template and the replacement are parsed with rslint_parser, they must parse or the process is exited instantly. 5: tree walking, we take the tree of the template and the tree of the file, for each descendant in the file root node, if the descendant has the same node type as the template we enter a separate walking function which will sequentially check each child in the tree and the template, if any child does not match 1 - 1 then the node is not a match, once it encounters something like 6: replacement, we take the binders bound in the fifth step and use rowan's replace_with function to make a new tree with the replacement but replaced with each binder. This could be very useful for a number of things:
|
Closing as it is implemented in #45 |
This issue is meant for housing any discussions and implementations of autofix. I believe we should implement autofix relatively soon, this way we won't have a huge burden to add autofix to every rule once there are way more rules. As far as i see it, there are two ways of doing autofix:
Purely text based has the issue of often not being very flexible (i personally disagree), and AST transformation based has the issue of not being flexible for minor text based fixes and needing AST serialization. Collectively autofix has fundamental issues which must be overcome:
All/Some rules need to be rerun after, i propose we add the ability to mark rules as fix-unsafe which will rerun only them, instead of needing all rules to rerun, which is expensive. This is absolutely required for stylistic rules, a fix cannot always conform to style and it should not either ways.
How do we choose what rule's fixes take precedence, if a rule wants to delete a node but another wants to change something in the node, which one do we trust?
Im not an expert in autofix so i would love some other ideas. RSLint's syntax again shines in this because it allows us to:
I for one think text based is the way to go, it allows for a wider range of edits, it provides a familiar UI to people who have used ESLint, and rslint's immutable tree makes it a little bit more difficult to apply AST transformations, moreover, RSLint does not have a way to make new nodes without the parser and serialize them.
As for implementation, i believe we should add a field to
RuleCtx
which isfixer: Option<Fixer>
, rules can make a new fixer (similar to ESLint) and add it, then the linter can simply collect the fixers and apply them. I also thought about making a new provided method onRuleCtx
but ended up scrapping it because rules will 100% want to know about context when fixing, so making it a separate method would mean that some of the linting logic would have to be repeated, this is very very ugly and i would rather not do that.I for one am not sure how to get over the issue of "which rule do we trust when it comes to a fix, do we trust the one who wants to change the node or the one who wants to delete it?". I also believe we should not try applying fixes if the parser returned errors, that can cause an absolute ton of confusing errors which i would rather not meddle in, error tolerance is great for linting but as soon as you try changing the AST produced (which is potentially semantically wrong) you end up with confusion.
The text was updated successfully, but these errors were encountered: