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
Fix: handle array-destructuring with obj assignment in prefer const #8994
Changes from all commits
08616bf
0048eb2
c21dad6
bd70c1c
da70054
695b006
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -126,6 +126,7 @@ function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) { | |
if (isReadBeforeInit) { | ||
return variable.defs[0].name; | ||
} | ||
|
||
return writer.identifier; | ||
} | ||
|
||
|
@@ -173,6 +174,7 @@ function groupByDestructuring(variables, ignoreReadBeforeAssign) { | |
const variable = variables[i]; | ||
const references = variable.references; | ||
const identifier = getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign); | ||
|
||
let prevId = null; | ||
|
||
for (let j = 0; j < references.length; ++j) { | ||
|
@@ -202,6 +204,128 @@ function groupByDestructuring(variables, ignoreReadBeforeAssign) { | |
return identifierMap; | ||
} | ||
|
||
/** | ||
* Returns a list of nodes | ||
* that have have the type Identifier. | ||
* This will search for nested Identifiers. | ||
* | ||
* @param {ASTNode} node - node to search for children and nested identifiers. | ||
* @returns {ASTNode[]} list Identifier nodes. | ||
*/ | ||
function getIdentifiersInArrayDestructureGroup(node) { | ||
const identifiers = []; | ||
|
||
if (node.elements) { | ||
const numberOfElements = node.elements.length; | ||
|
||
for (let i = 0; i < numberOfElements; i++) { | ||
if (!node.elements[i].elements && node.elements[i].type === "Identifier") { | ||
identifiers.push(node.elements[i]); | ||
} else { | ||
const innerIdentifiers = getIdentifiersInArrayDestructureGroup(node.elements[i]); | ||
|
||
innerIdentifiers.forEach(identifierNode => identifiers.push(identifierNode)); | ||
} | ||
} | ||
} | ||
return identifiers; | ||
} | ||
|
||
/** | ||
* Returns a count of nodes | ||
* This will count nested nodes in nodes elements attribute. | ||
* | ||
* @param {ASTNode} node - node to count elements. | ||
* @returns {integer} count nodes. | ||
*/ | ||
function countElementsInArrayDestructureGroup(node) { | ||
let count = 0; | ||
|
||
if (node.elements) { | ||
const numberOfElements = node.elements.length; | ||
|
||
for (let i = 0; i < numberOfElements; i++) { | ||
if (!node.elements[i].elements) { | ||
count += 1; | ||
} else { | ||
count += countElementsInArrayDestructureGroup(node.elements[i]); | ||
} | ||
} | ||
} | ||
return count; | ||
} | ||
|
||
/** | ||
* Returns a list of nodes | ||
* that have have the value type Identifier. | ||
* This will search for nested Identifiers. | ||
* | ||
* @param {ASTNode} node - node to search properties that have value Identifier. | ||
* @returns {ASTNode[]} list Identifier nodes. | ||
*/ | ||
function getIdentifiersInObjectDestructureGroup(node) { | ||
const identifier = []; | ||
const propertiesLength = node.properties.length; | ||
|
||
for (let i = 0; i < propertiesLength; i++) { | ||
if (node.properties[i].value.type === "Identifier") { | ||
identifier.push(node.properties[i].value); | ||
} | ||
} | ||
return identifier; | ||
} | ||
|
||
/** | ||
* Returns a count of nodes | ||
* This will count the node properties length | ||
* | ||
* @param {ASTNode} node - node to count properties. | ||
* @returns {integer} count properties length. | ||
*/ | ||
function countElementsInObjectDestructureGroup(node) { | ||
return node.properties.length; | ||
} | ||
|
||
/** | ||
* Checks to see if there are less identifier nodes in a destructure group | ||
* than the number of terms in the group. It might mean that there | ||
* is a term in the group that cannot be converted to const. | ||
* We want to make sure all terms can be reported | ||
* If not, we should remove the terms that would be reported in "any" case | ||
* | ||
* @param {Map<ASTNode, ASTNode[]>} identifierMap - Variables to group by destructuring. | ||
* @param {boolean} checkingMixedDestructuring - boolean to check if any value in destructuring | ||
* should use const | ||
* @param {integer|null} destructureCount count of destructure terms. | ||
* @param {ASTNode[]|null} destructureIdentifier list of Identifier nodes to check | ||
* in IdentifierMap | ||
* @returns {Map<ASTNode, ASTNode[]>} Grouped identifier nodes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you make this description of the return type more detailed? For example, how are the nodes grouped? |
||
*/ | ||
function verifyAllDestructuring(identifierMap, checkingMixedDestructuring, destructureCount, destructureIdentifier) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be possible for this function to return a new |
||
if (checkingMixedDestructuring) { | ||
return identifierMap; | ||
} | ||
if (destructureCount === null && destructureIdentifier === null) { | ||
return identifierMap; | ||
} | ||
if (destructureIdentifier.length < destructureCount) { | ||
for (const key of identifierMap.keys()) { | ||
const destructureGroup = identifierMap.get(key); | ||
const destructureElement = destructureGroup[0]; | ||
|
||
if (destructureIdentifier !== null) { | ||
for (let i = 0; i < destructureIdentifier.length; i++) { | ||
if (destructureIdentifier[i] === destructureElement) { | ||
identifierMap.delete(key); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return identifierMap; | ||
} | ||
|
||
/** | ||
* Finds the nearest parent of node with a given type. | ||
* | ||
|
@@ -252,6 +376,8 @@ module.exports = { | |
const checkingMixedDestructuring = options.destructuring !== "all"; | ||
const ignoreReadBeforeAssign = options.ignoreReadBeforeAssign === true; | ||
const variables = []; | ||
let destructureCount = null; | ||
let destructureIdentifier = null; | ||
|
||
/** | ||
* Reports given identifier nodes if all of the nodes should be declared | ||
|
@@ -300,7 +426,18 @@ module.exports = { | |
|
||
return { | ||
"Program:exit"() { | ||
groupByDestructuring(variables, ignoreReadBeforeAssign).forEach(checkGroup); | ||
const identifierMap = groupByDestructuring(variables, ignoreReadBeforeAssign); | ||
|
||
verifyAllDestructuring(identifierMap, checkingMixedDestructuring, destructureCount, destructureIdentifier).forEach(checkGroup); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't seem right to me -- |
||
}, | ||
"ExpressionStatement[expression.left.type = /ArrayPattern|ObjectPattern/]"(node) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be |
||
if (node.expression.left.type === "ArrayPattern") { | ||
destructureIdentifier = getIdentifiersInArrayDestructureGroup(node.expression.left); | ||
destructureCount = countElementsInArrayDestructureGroup(node.expression.left); | ||
} else { | ||
destructureIdentifier = getIdentifiersInObjectDestructureGroup(node.expression.left); | ||
destructureCount = countElementsInObjectDestructureGroup(node.expression.left); | ||
} | ||
}, | ||
|
||
VariableDeclaration(node) { | ||
|
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'm a bit confused about this line -- is the
!node.elements[i].elements
part redundant? It seems like anIdentifier
node would never have anelements
property.