Skip to content

Commit c147873

Browse files
danilsomsikovDevtools-frontend LUCI CQ
authored andcommitted
Reduce the number of casts to Node in preferTemplateLiterals
The problem is that ESLint defines a `parent` property on the estree Node. However specific estree Node subtypes, such as MemberExpression, have properties with estree Node subtypes, such as Identifier. The value in practice does have a `parent` property but this is not reflected in types. This CL makes subrules unaware of the difference by exposing only the estree type to them and makes "core" libraries do the casting where necessary. Bug: 400353541 Change-Id: I84db45bb35bbfb09f0d6bbde8f730d0f61f09c71 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6400633 Commit-Queue: Danil Somsikov <[email protected]> Reviewed-by: Philip Pfaffe <[email protected]>
1 parent 555cc4b commit c147873

File tree

4 files changed

+38
-32
lines changed

4 files changed

+38
-32
lines changed

scripts/eslint_rules/lib/no-imperative-dom-api.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ const {DomFragment} = require('./no-imperative-dom-api/dom-fragment.js');
1717
const toolbar = require('./no-imperative-dom-api/toolbar.js');
1818
const widget = require('./no-imperative-dom-api/widget.js');
1919

20-
/** @typedef {import('eslint').Rule.Node} Node */
20+
/** @typedef {import('estree').Node} Node */
21+
/** @typedef {import('eslint').Rule.Node} EsLintNode */
2122
/** @typedef {import('eslint').AST.SourceLocation} SourceLocation */
2223
/** @typedef {import('eslint').Scope.Variable} Variable */
2324
/** @typedef {import('eslint').Scope.Reference} Reference*/
@@ -64,31 +65,31 @@ module.exports = {
6465
}
6566

6667
/**
67-
* @param {Node} reference
68+
* @param {EsLintNode} reference
6869
* @param {DomFragment} domFragment
6970
*/
7071
function processReference(reference, domFragment) {
7172
const parent = reference.parent;
7273
const isAccessed = parent.type === 'MemberExpression' && parent.object === reference;
73-
const property = isAccessed ? /** @type {Node} */ (parent.property) : null;
74+
const property = isAccessed ? parent.property : null;
7475
const grandParent = parent.parent;
7576
const isPropertyAssignment =
7677
isAccessed && grandParent.type === 'AssignmentExpression' && grandParent.left === parent;
77-
const propertyValue = isPropertyAssignment ? /** @type {Node} */(grandParent.right) : null;
78+
const propertyValue = isPropertyAssignment ? grandParent.right : null;
7879
const isMethodCall = isAccessed && grandParent.type === 'CallExpression' && grandParent.callee === parent;
79-
const firstArg = isMethodCall ? /** @type {Node} */(grandParent.arguments[0]) : null;
80-
const secondArg = isMethodCall ? /** @type {Node} */(grandParent.arguments[1]) : null;
80+
const firstArg = isMethodCall ? grandParent.arguments[0] : null;
81+
const secondArg = isMethodCall ? grandParent.arguments[1] : null;
8182
const grandGrandParent = grandParent.parent;
8283
const isPropertyMethodCall = isAccessed && grandParent.type === 'MemberExpression' &&
8384
grandParent.object === parent && grandGrandParent.type === 'CallExpression' &&
8485
grandGrandParent.callee === grandParent;
85-
const propertyMethodArgument = isPropertyMethodCall ? /** @type {Node} */ (grandGrandParent.arguments[0]) : null;
86+
const propertyMethodArgument = isPropertyMethodCall ? grandGrandParent.arguments[0] : null;
8687
const isSubpropertyAssignment = isAccessed && grandParent.type === 'MemberExpression' &&
8788
grandParent.object === parent && grandParent.property.type === 'Identifier' &&
8889
grandGrandParent.type === 'AssignmentExpression' && grandGrandParent.left === grandParent;
8990
const subproperty =
9091
isSubpropertyAssignment && grandParent.property.type === 'Identifier' ? grandParent.property : null;
91-
const subpropertyValue = isSubpropertyAssignment ? /** @type {Node} */ (grandGrandParent.right) : null;
92+
const subpropertyValue = isSubpropertyAssignment ? grandGrandParent.right : null;
9293
for (const rule of subrules) {
9394
if (isPropertyAssignment) {
9495
rule.propertyAssignment?.(property, propertyValue, domFragment);
@@ -151,7 +152,7 @@ module.exports = {
151152
node: domFragment.replacementLocation,
152153
messageId: 'preferTemplateLiterals',
153154
fix(fixer) {
154-
let replacementLocation = /** @type {Node} */(domFragment.replacementLocation);
155+
let replacementLocation = domFragment.replacementLocation;
155156
if (replacementLocation.parent.type === 'ExportNamedDeclaration') {
156157
replacementLocation = replacementLocation.parent;
157158
}

scripts/eslint_rules/lib/no-imperative-dom-api/adorner.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,18 @@ module.exports = {
2727
if (property.type !== 'Property') {
2828
continue;
2929
}
30-
const key = /** @type {Node} */ (property.key);
31-
if (isIdentifier(key, 'name')) {
30+
if (isIdentifier(property.key, 'name')) {
3231
domFragment.attributes.push({
3332
key: 'aria-label',
34-
value: /** @type {Node} */ (property.value),
33+
value: property.value,
3534
});
3635
}
37-
if (isIdentifier(key, 'jslogContext')) {
36+
if (isIdentifier(property.key, 'jslogContext')) {
3837
domFragment.attributes.push(
3938
{key: 'jslog', value: '${VisualLogging.adorner(' + sourceCode.getText(property.value) + ')}'});
4039
}
41-
if (isIdentifier(key, 'content')) {
42-
const childFragment = DomFragment.getOrCreate(/** @type {Node} */ (property.value), sourceCode);
40+
if (isIdentifier(property.key, 'content')) {
41+
const childFragment = DomFragment.getOrCreate(property.value, sourceCode);
4342
childFragment.parent = domFragment;
4443
domFragment.children.push(childFragment);
4544
}

scripts/eslint_rules/lib/no-imperative-dom-api/ast.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
*/
77
'use strict';
88

9-
/** @typedef {import('eslint').Rule.Node} Node */
9+
/** @typedef {import('estree').Node} Node */
10+
/** @typedef {import('eslint').Rule.Node} EsLintNode */
1011

1112
/**
1213
* @param {Node} node
@@ -23,12 +24,12 @@ function isIdentifier(node, name) {
2324
* @param {function(Node): boolean} propertyPredicate
2425
*/
2526
function isMemberExpression(node, objectPredicate, propertyPredicate) {
26-
return node.type === 'MemberExpression' && objectPredicate(/** @type {Node} */ (node.object)) &&
27-
propertyPredicate(/** @type {Node} */ (node.property));
27+
return node.type === 'MemberExpression' && objectPredicate(node.object) && propertyPredicate(node.property);
2828
}
2929

30-
/** @param {Node} node */
31-
function getEnclosingExpression(node) {
30+
/** @param {Node} estreeNode */
31+
function getEnclosingExpression(estreeNode) {
32+
let node = /** @type {EsLintNode} */ (estreeNode);
3233
while (node.parent) {
3334
if (node.parent.type === 'BlockStatement') {
3435
return node;
@@ -38,17 +39,19 @@ function getEnclosingExpression(node) {
3839
return null;
3940
}
4041

41-
function getEnclosingProperty(node) {
42+
/** @param {Node} estreeNode */
43+
function getEnclosingProperty(estreeNode) {
44+
const node = /** @type {EsLintNode} */ (estreeNode);
4245
if (node.parent.type === 'AssignmentExpression' && node.parent.right === node &&
4346
isMemberExpression(node.parent.left, n => n.type === 'ThisExpression', n => n.type === 'Identifier')) {
44-
return /** @type {Node} */ (node.parent.left);
47+
return node.parent.left;
4548
}
4649
return null;
4750
}
4851

4952
/** @param {Node} node */
5053
function getEnclosingClassDeclaration(node) {
51-
let parent = node.parent;
54+
let parent = /** @type {EsLintNode} */ (node).parent;
5255
while (parent && parent.type !== 'ClassDeclaration') {
5356
parent = parent.parent;
5457
}

scripts/eslint_rules/lib/no-imperative-dom-api/dom-fragment.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
const {getEnclosingProperty, getEnclosingClassDeclaration} = require('./ast.js');
1010

11-
/** @typedef {import('eslint').Rule.Node} Node */
11+
/** @typedef {import('estree').Node} Node */
12+
/** @typedef {import('eslint').Rule.Node} EsLintNode */
1213
/** @typedef {import('eslint').Scope.Variable} Variable */
1314
/** @typedef {import('eslint').SourceCode} SourceCode */
1415

@@ -26,15 +27,16 @@ class DomFragment {
2627
/** @type {DomFragment[]} */ children = [];
2728
/** @type {DomFragment|undefined} */ parent;
2829
/** @type {string|undefined} */ expression;
29-
/** @type {Node|undefined} */ replacementLocation;
30-
/** @type {Node[]} */ references = [];
30+
/** @type {EsLintNode|undefined} */ replacementLocation;
31+
/** @type {EsLintNode[]} */ references = [];
3132

3233
/**
33-
* @param {Node} node
34+
* @param {Node} estreeNode
3435
* @param {SourceCode} sourceCode
3536
* @return {DomFragment}
3637
*/
37-
static getOrCreate(node, sourceCode) {
38+
static getOrCreate(estreeNode, sourceCode) {
39+
const node = /** @type {EsLintNode} */ (estreeNode);
3840
const variable = getEnclosingVariable(node, sourceCode);
3941
const key = variable ?? sourceCode.getText(getEnclosingProperty(node) ?? node);
4042

@@ -43,8 +45,8 @@ class DomFragment {
4345
result = new DomFragment();
4446
domFragments.set(key, result);
4547
if (variable) {
46-
result.references = variable.references.map(r => (/** @type {Node} */ (r.identifier)));
47-
result.references.push(/** @type {Node} */ (variable.identifiers[0]));
48+
result.references = variable.references.map(r => (/** @type {EsLintNode} */ (r.identifier)));
49+
result.references.push(/** @type {EsLintNode} */ (variable.identifiers[0]));
4850
} else {
4951
result.expression = sourceCode.getText(node);
5052
const classDeclaration = getEnclosingClassDeclaration(node);
@@ -146,11 +148,12 @@ class DomFragment {
146148
}
147149

148150
/**
149-
* @param {Node} node
151+
* @param {Node} estreeNode
150152
* @param {SourceCode} sourceCode
151153
* @return {Variable|null}
152154
*/
153-
function getEnclosingVariable(node, sourceCode) {
155+
function getEnclosingVariable(estreeNode, sourceCode) {
156+
const node = /** @type {EsLintNode} */ (estreeNode);
154157
if (node.type === 'Identifier') {
155158
let scope = sourceCode.getScope(node);
156159
const variableName = node.name;

0 commit comments

Comments
 (0)