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
feat!: move AST traversal into SourceCode #18167
Conversation
✅ Deploy Preview for docs-eslint canceled.
|
TSC Summary: As part of this work, we are moving the AST traversal from In our codebase we have three rules that broke: TSC Question: This would be another breaking change for v9.0.0. Are we okay with that? |
lib/source-code/source-code.js
Outdated
enterNode(node, parent) { | ||
steps.push(new TraversalStep({ | ||
type: "visit", | ||
target: node, | ||
phase: 1, | ||
args: [node, parent] | ||
})); | ||
}, | ||
leaveNode(node, parent) { | ||
steps.push(new TraversalStep({ | ||
type: "visit", | ||
target: node, | ||
phase: 2, | ||
args: [node, parent] | ||
})); |
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.
Questions:
- Do these
enterNode
andleaveNode
ever get called with theparent
argument? - Are we using
args
property oftype: "visit"
steps anywhere?
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.
Good questions. I need to look into 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.
The answers are:
- No, they don't, so I'll update that.
- We aren't using
args
right now because everything is going throughNodeEventGenerator
, but I'd still like to keep these here as the foundation for the rewrite.
Traverser.traverse(this.ast, { | ||
enter(node, parent) { | ||
|
||
// save the parent node on a property for backwards compatibility | ||
node.parent = parent; | ||
|
||
analyzer.enterNode(node); | ||
}, | ||
leave(node) { | ||
analyzer.leaveNode(node); | ||
}, | ||
visitorKeys: this.visitorKeys | ||
}); |
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.
Are we sure that the current implementation of code path analysis doesn't expect parent
property to be pre-set on all nodes (including not yet traversed ones)?
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 can only go by all of the tests that are passing. I'm assuming if there was a timing issue, that something would break.
|
||
**To address:** If you are accessing any array properties on `CodePath` or `CodePathSegment`, you'll need to update your code. Specifically: | ||
|
||
* If you are checking the `length` of any array properties, ensure you are using relative comparison operators like `<`, `>`, `<=`, and `>=` instead of equals. |
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 too general to understand the advice, I think we should leave just the second bullet (if you're accessing nextSegments
and others, verify that your code will still work as expected).
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 one of the bugs I ran into when implementing this, where we were doing === 1
in one spot. I think it's worth calling this out because it took me a while to realize why that was a problem.
lib/rules/constructor-super.js
Outdated
if (segment.nextSegments.length === 1 && | ||
if (segment.nextSegments.length && |
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 believe this introduces new false negatives. For example:
/* eslint constructor-super: "error" */
class C extends D {
// no errors here
constructor() {
do {
something();
} while (foo);
}
}
I'm not sure exactly what this condition was targeting. When we figure that out, it would be good to explain it better 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.
If I comment out this condition altogether, these valid tests fails:
class A extends B { constructor(a) { super(); for (const b of a) { this.a(); } } }:
class A extends Object {
constructor() {
super();
for (let i = 0; i < 0; i++);
}
}
class A extends B {
constructor(props) {
super(props);
try {
let arr = [];
for (let a of arr) {
}
} catch (err) {
}
}
}
So it seems like it's designed to catch loops that occur after super()
.
I couldn't figure out anything else about how this rule works, though, so any ideas would be greatly appreciated.
Per 2024-03-07 TSC meeting, this will be included in v9. |
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.
LGTM, thanks!
Prerequisites checklist
What is the purpose of this pull request? (put an "X" next to an item)
[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofix to a rule
[ ] Add a CLI option
[x] Add something to the core
[ ] Other, please explain:
What changes did you make? (Give an overview)
Linter
and intoSourceCode#traverse()
.no-useless-returns
,constructor-super
, andno-this-before-super
to they still work. These rules were also tested against v8.57.0 to ensure that such changes would be backwards compatible.Refs #16999
Is there anything you'd like reviewers to focus on?
I deviated from the RFC in how I represented the traversal steps. I realized while implementing that relying on
type
andphase
as string comparisons would be a big performance hit, so I added thekind
property as a number (keepingtype
for easier debugging, though not sure if that's needed) and switchedphase
to a number. That saves us a lot of string comparisons during traversal.