Skip to content

Commit 07193c2

Browse files
committed
feat(linter/plugins): implement SourceCode#getAncestors (#14346)
Implement `sourceCode.getAncestors` method to get all the ancestors of an AST node.
1 parent c8de6fe commit 07193c2

File tree

3 files changed

+75
-46
lines changed

3 files changed

+75
-46
lines changed

apps/oxlint/src-js/plugins/source_code.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -518,16 +518,7 @@ export const SOURCE_CODE = Object.freeze({
518518
throw new Error('`sourceCode.commentsExistBetween` not implemented yet'); // TODO
519519
},
520520

521-
/**
522-
* Get all the ancestors of a given node.
523-
* @param node - AST node
524-
* @returns All the ancestor nodes in the AST, not including the provided node,
525-
* starting from the root node at index 0 and going inwards to the parent node.
526-
*/
527-
// oxlint-disable-next-line no-unused-vars
528-
getAncestors(node: Node): Node[] {
529-
throw new Error('`sourceCode.getAncestors` not implemented yet'); // TODO
530-
},
521+
getAncestors,
531522

532523
/**
533524
* Get the variables that `node` defines.
@@ -655,6 +646,26 @@ export function getIndexFromLoc(loc: LineColumn): number {
655646
throw new TypeError('Expected `loc` to be an object with integer `line` and `column` properties.');
656647
}
657648

649+
/**
650+
* Get all the ancestors of a given node.
651+
* @param node - AST node
652+
* @returns All the ancestor nodes in the AST, not including the provided node,
653+
* starting from the root node at index 0 and going inwards to the parent node.
654+
*/
655+
function getAncestors(node: Node): Node[] {
656+
const ancestors = [];
657+
658+
for (
659+
let ancestor = (node as unknown as { parent: Node }).parent;
660+
ancestor;
661+
ancestor = (ancestor as unknown as { parent: Node }).parent
662+
) {
663+
ancestors.push(ancestor);
664+
}
665+
666+
return ancestors.reverse();
667+
}
668+
658669
// Options for various `SourceCode` methods e.g. `getFirstToken`.
659670
export interface SkipOptions {
660671
// Number of skipping tokens

apps/oxlint/test/fixtures/parent/output.snap.md

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,97 @@
33

44
# stdout
55
```
6-
x parents(check): VariableDeclaration -> Program
6+
x parents(check): VariableDeclaration:
7+
| parent: Program
8+
| ancestors: [ Program ]
79
,-[files/index.js:1:1]
810
1 | const obj = { a: [b, c], ...d };
911
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1012
`----
1113
12-
x parents(check): Program -> null
14+
x parents(check): Program:
15+
| parent: undefined
16+
| ancestors: [ ]
1317
,-[files/index.js:1:1]
1418
1 | const obj = { a: [b, c], ...d };
1519
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1620
`----
1721
18-
x parents(check): Identifier -> VariableDeclarator
22+
x parents(check): Identifier:
23+
| parent: VariableDeclarator
24+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator ]
1925
,-[files/index.js:1:7]
2026
1 | const obj = { a: [b, c], ...d };
2127
: ^^^
2228
`----
2329
24-
x parents(check): VariableDeclarator -> VariableDeclaration
30+
x parents(check): VariableDeclarator:
31+
| parent: VariableDeclaration
32+
| ancestors: [ Program, VariableDeclaration ]
2533
,-[files/index.js:1:7]
2634
1 | const obj = { a: [b, c], ...d };
2735
: ^^^^^^^^^^^^^^^^^^^^^^^^^
2836
`----
2937
30-
x parents(check): ObjectExpression -> VariableDeclarator
38+
x parents(check): ObjectExpression:
39+
| parent: VariableDeclarator
40+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator ]
3141
,-[files/index.js:1:13]
3242
1 | const obj = { a: [b, c], ...d };
3343
: ^^^^^^^^^^^^^^^^^^^
3444
`----
3545
36-
x parents(check): Identifier -> Property
46+
x parents(check): Identifier:
47+
| parent: Property
48+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, Property ]
3749
,-[files/index.js:1:15]
3850
1 | const obj = { a: [b, c], ...d };
3951
: ^
4052
`----
4153
42-
x parents(check): Property -> ObjectExpression
54+
x parents(check): Property:
55+
| parent: ObjectExpression
56+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression ]
4357
,-[files/index.js:1:15]
4458
1 | const obj = { a: [b, c], ...d };
4559
: ^^^^^^^^^
4660
`----
4761
48-
x parents(check): ArrayExpression -> Property
62+
x parents(check): ArrayExpression:
63+
| parent: Property
64+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, Property ]
4965
,-[files/index.js:1:18]
5066
1 | const obj = { a: [b, c], ...d };
5167
: ^^^^^^
5268
`----
5369
54-
x parents(check): Identifier -> ArrayExpression
70+
x parents(check): Identifier:
71+
| parent: ArrayExpression
72+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, Property, ArrayExpression ]
5573
,-[files/index.js:1:19]
5674
1 | const obj = { a: [b, c], ...d };
5775
: ^
5876
`----
5977
60-
x parents(check): Identifier -> ArrayExpression
78+
x parents(check): Identifier:
79+
| parent: ArrayExpression
80+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, Property, ArrayExpression ]
6181
,-[files/index.js:1:22]
6282
1 | const obj = { a: [b, c], ...d };
6383
: ^
6484
`----
6585
66-
x parents(check): SpreadElement -> ObjectExpression
86+
x parents(check): SpreadElement:
87+
| parent: ObjectExpression
88+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression ]
6789
,-[files/index.js:1:26]
6890
1 | const obj = { a: [b, c], ...d };
6991
: ^^^^
7092
`----
7193
72-
x parents(check): Identifier -> SpreadElement
94+
x parents(check): Identifier:
95+
| parent: SpreadElement
96+
| ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, SpreadElement ]
7397
,-[files/index.js:1:29]
7498
1 | const obj = { a: [b, c], ...d };
7599
: ^

apps/oxlint/test/fixtures/parent/plugin.ts

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,25 @@ const plugin: Plugin = {
77
rules: {
88
check: {
99
create(context) {
10+
function reportAncestry(node: any) {
11+
context.report({
12+
message: `${node.type}:\n` +
13+
`parent: ${node.parent?.type}\n` +
14+
// @ts-ignore
15+
`ancestors: [ ${context.sourceCode.getAncestors(node).map(node => node.type).join(', ')} ]`,
16+
node,
17+
});
18+
}
19+
1020
return {
11-
Program(node) {
12-
context.report({ message: `${node.type} -> ${node.parent}`, node });
13-
},
14-
VariableDeclaration(node) {
15-
context.report({ message: `${node.type} -> ${node.parent.type}`, node });
16-
},
17-
VariableDeclarator(node) {
18-
context.report({ message: `${node.type} -> ${node.parent.type}`, node });
19-
},
20-
Identifier(node) {
21-
context.report({ message: `${node.type} -> ${node.parent.type}`, node });
22-
},
23-
ObjectExpression(node) {
24-
context.report({ message: `${node.type} -> ${node.parent.type}`, node });
25-
},
26-
Property(node) {
27-
context.report({ message: `${node.type} -> ${node.parent.type}`, node });
28-
},
29-
ArrayExpression(node) {
30-
context.report({ message: `${node.type} -> ${node.parent.type}`, node });
31-
},
32-
SpreadElement(node) {
33-
context.report({ message: `${node.type} -> ${node.parent.type}`, node });
34-
},
21+
Program: reportAncestry,
22+
VariableDeclaration: reportAncestry,
23+
VariableDeclarator: reportAncestry,
24+
Identifier: reportAncestry,
25+
ObjectExpression: reportAncestry,
26+
Property: reportAncestry,
27+
ArrayExpression: reportAncestry,
28+
SpreadElement: reportAncestry,
3529
};
3630
},
3731
},

0 commit comments

Comments
 (0)