}
+ /**
+ * Checks whether a node is a sibling of the rest property or not.
+ * @param {ASTNode} node a node to check
+ * @returns {boolean} True if the node is a sibling of the rest property, otherwise false.
+ */
+ function hasRestSibling(node) {
+ return node.type === "Property" &&
+ node.parent.type === "ObjectPattern" &&
+ REST_PROPERTY_TYPE.test(node.parent.properties[node.parent.properties.length - 1].type);
+ }
+
/**
* Determines if a variable has a sibling rest property
* @param {Variable} variable eslint-scope variable object.
*/
function hasRestSpreadSibling(variable) {
if (config.ignoreRestSiblings) {
- return variable.defs.some(def => {
- const propertyNode = def.name.parent;
- const patternNode = propertyNode.parent;
-
- return (
- propertyNode.type === "Property" &&
- patternNode.type === "ObjectPattern" &&
- REST_PROPERTY_TYPE.test(patternNode.properties[patternNode.properties.length - 1].type)
- );
- });
+ const hasRestSiblingDefinition = variable.defs.some(def => hasRestSibling(def.name.parent));
+ const hasRestSiblingReference = variable.references.some(ref => hasRestSibling(ref.identifier.parent));
+
+ return hasRestSiblingDefinition || hasRestSiblingReference;
}
return false;
);
}
+ /**
+ * Checks whether a given node is unused expression or not.
+ * @param {ASTNode} node The node itself
+ * @returns {boolean} The node is an unused expression.
+ * @private
+ */
+ function isUnusedExpression(node) {
+ const parent = node.parent;
+
+ if (parent.type === "ExpressionStatement") {
+ return true;
+ }
+
+ if (parent.type === "SequenceExpression") {
+ const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;
+
+ if (!isLastExpression) {
+ return true;
+ }
+ return isUnusedExpression(parent);
+ }
+
+ return false;
+ }
+
/**
* Checks whether a given reference is a read to update itself or not.
* @param {eslint-scope.Reference} ref A reference to check.
function isReadForItself(ref, rhsNode) {
const id = ref.identifier;
const parent = id.parent;
- const grandparent = parent.parent;
return ref.isRead() && (
// self update. e.g. `a += 1`, `a++`
- (// in RHS of an assignment for itself. e.g. `a = a + 1`
- ((
+ (
+ (
parent.type === "AssignmentExpression" &&
- grandparent.type === "ExpressionStatement" &&
- parent.left === id
+ parent.left === id &&
+ isUnusedExpression(parent)
) ||
+ (
+ parent.type === "UpdateExpression" &&
+ isUnusedExpression(parent)
+ )
+ ) ||
+
+ // in RHS of an assignment for itself. e.g. `a = a + 1`
(
- parent.type === "UpdateExpression" &&
- grandparent.type === "ExpressionStatement"
- ) || rhsNode &&
- isInside(id, rhsNode) &&
- !isInsideOfStorableFunction(id, rhsNode)))
+ rhsNode &&
+ isInside(id, rhsNode) &&
+ !isInsideOfStorableFunction(id, rhsNode)
+ )
);
}
// Report the first declaration.
if (unusedVar.defs.length > 0) {
+
+ // report last write reference, https://github.com/eslint/eslint/issues/14324
+ const writeReferences = unusedVar.references.filter(ref => ref.isWrite() && ref.from.variableScope === unusedVar.scope.variableScope);
+
+ let referenceToReport;
+
+ if (writeReferences.length > 0) {
+ referenceToReport = writeReferences[writeReferences.length - 1];
+ }
+
context.report({
- node: unusedVar.references.length ? unusedVar.references[
- unusedVar.references.length - 1
- ].identifier : unusedVar.identifiers[0],
+ node: referenceToReport ? referenceToReport.identifier : unusedVar.identifiers[0],
messageId: "unusedVar",
data: unusedVar.references.some(ref => ref.isWrite())
? getAssignedMessageData(unusedVar)