| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 | 'use strict';const {methodCallSelector, matches, memberExpressionSelector} = require('./selectors/index.js');const isSameReference = require('./utils/is-same-reference.js');const {getParenthesizedRange} = require('./utils/parentheses.js');const messages = {	'non-zero': 'The non-empty check is useless as `Array#some()` returns `false` for an empty array.',	zero: 'The empty check is useless as `Array#every()` returns `true` for an empty array.',};const logicalExpressionSelector = [	'LogicalExpression',	matches(['[operator="||"]', '[operator="&&"]']),].join('');// We assume the user already follows `unicorn/explicit-length-check`. These are allowed in that rule.const lengthCompareZeroSelector = [	logicalExpressionSelector,	' > ',	'BinaryExpression',	memberExpressionSelector({path: 'left', property: 'length'}),	'[right.type="Literal"]',	'[right.raw="0"]',].join('');const zeroLengthCheckSelector = [	lengthCompareZeroSelector,	'[operator="==="]',].join('');const nonZeroLengthCheckSelector = [	lengthCompareZeroSelector,	matches(['[operator=">"]', '[operator="!=="]']),].join('');const arraySomeCallSelector = methodCallSelector('some');const arrayEveryCallSelector = methodCallSelector('every');function flatLogicalExpression(node) {	return [node.left, node.right].flatMap(child =>		child.type === 'LogicalExpression' && child.operator === node.operator			? flatLogicalExpression(child)			: [child],	);}/** @param {import('eslint').Rule.RuleContext} context */const create = context => {	const logicalExpressions = [];	const zeroLengthChecks = new Set();	const nonZeroLengthChecks = new Set();	const arraySomeCalls = new Set();	const arrayEveryCalls = new Set();	function isUselessLengthCheckNode({node, operator, siblings}) {		return (			(				operator === '||'				&& zeroLengthChecks.has(node)				&& siblings.some(condition =>					arrayEveryCalls.has(condition)					&& isSameReference(node.left.object, condition.callee.object),				)			)			|| (				operator === '&&'				&& nonZeroLengthChecks.has(node)				&& siblings.some(condition =>					arraySomeCalls.has(condition)					&& isSameReference(node.left.object, condition.callee.object),				)			)		);	}	function getUselessLengthCheckNode(logicalExpression) {		const {operator} = logicalExpression;		return flatLogicalExpression(logicalExpression)			.filter((node, index, conditions) => isUselessLengthCheckNode({				node,				operator,				siblings: [					conditions[index - 1],					conditions[index + 1],				].filter(Boolean),			}));	}	return {		[zeroLengthCheckSelector](node) {			zeroLengthChecks.add(node);		},		[nonZeroLengthCheckSelector](node) {			nonZeroLengthChecks.add(node);		},		[arraySomeCallSelector](node) {			arraySomeCalls.add(node);		},		[arrayEveryCallSelector](node) {			arrayEveryCalls.add(node);		},		[logicalExpressionSelector](node) {			logicalExpressions.push(node);		},		* 'Program:exit'() {			const nodes = new Set(				logicalExpressions.flatMap(logicalExpression =>					getUselessLengthCheckNode(logicalExpression),				),			);			for (const node of nodes) {				yield {					loc: {						start: node.left.property.loc.start,						end: node.loc.end,					},					messageId: zeroLengthChecks.has(node) ? 'zero' : 'non-zero',					/** @param {import('eslint').Rule.RuleFixer} fixer */					fix(fixer) {						const sourceCode = context.getSourceCode();						const {left, right} = node.parent;						const leftRange = getParenthesizedRange(left, sourceCode);						const rightRange = getParenthesizedRange(right, sourceCode);						const range = [];						if (left === node) {							range[0] = leftRange[0];							range[1] = rightRange[0];						} else {							range[0] = leftRange[1];							range[1] = rightRange[1];						}						return fixer.removeRange(range);					},				};			}		},	};};/** @type {import('eslint').Rule.RuleModule} */module.exports = {	create,	meta: {		type: 'suggestion',		docs: {			description: 'Disallow useless array length check.',		},		fixable: 'code',		messages,	},};
 |