oxlint-plugin-react-doctor 0.7.7-dev.bdd1321 → 0.7.7-dev.c79c897
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +433 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11003,6 +11003,14 @@ const PROMISE_CHAIN_METHOD_NAMES$1 = new Set([
|
|
|
11003
11003
|
"catch",
|
|
11004
11004
|
"finally"
|
|
11005
11005
|
]);
|
|
11006
|
+
const isPromiseChainCall = (callee) => isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && PROMISE_CHAIN_METHOD_NAMES$1.has(callee.property.name) && isNodeOfType(stripParenExpression(callee.object), "CallExpression");
|
|
11007
|
+
const getPromiseChainCallForCallback = (candidate) => {
|
|
11008
|
+
let callbackContainer = candidate.parent;
|
|
11009
|
+
while (callbackContainer && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(callbackContainer.type)) callbackContainer = callbackContainer.parent;
|
|
11010
|
+
if (!isNodeOfType(callbackContainer, "CallExpression")) return null;
|
|
11011
|
+
if (!callbackContainer.arguments?.some((argument) => stripParenExpression(argument) === candidate)) return null;
|
|
11012
|
+
return isPromiseChainCall(stripParenExpression(callbackContainer.callee)) ? callbackContainer : null;
|
|
11013
|
+
};
|
|
11006
11014
|
const collectEffectInvokedFunctions = (effectCallback) => {
|
|
11007
11015
|
const invokedFunctions = new Set([effectCallback]);
|
|
11008
11016
|
const localFunctionBindings = /* @__PURE__ */ new Map();
|
|
@@ -11014,7 +11022,6 @@ const collectEffectInvokedFunctions = (effectCallback) => {
|
|
|
11014
11022
|
invokedFunctions.add(strippedCandidate);
|
|
11015
11023
|
pendingFunctions.push(strippedCandidate);
|
|
11016
11024
|
};
|
|
11017
|
-
const isPromiseChainCall = (callee) => isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && PROMISE_CHAIN_METHOD_NAMES$1.has(callee.property.name) && isNodeOfType(stripParenExpression(callee.object), "CallExpression");
|
|
11018
11025
|
while (pendingFunctions.length > 0) {
|
|
11019
11026
|
const currentFunction = pendingFunctions.pop();
|
|
11020
11027
|
if (!currentFunction) break;
|
|
@@ -11844,6 +11851,94 @@ const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
|
|
|
11844
11851
|
return didFindUnmountCleanup;
|
|
11845
11852
|
};
|
|
11846
11853
|
const hasSplitLifecycleCleanup = (callback, usage, context) => usage.handleKey !== null && hasRerunReleaseBeforeUsage(callback, usage, context) && hasStableUnmountCleanupForUsage(callback, usage, context);
|
|
11854
|
+
const collectBlockingBooleanStates = (expression, blockedExpressionValue, context) => {
|
|
11855
|
+
const unwrappedExpression = stripParenExpression(expression);
|
|
11856
|
+
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return collectBlockingBooleanStates(unwrappedExpression.argument, !blockedExpressionValue, context);
|
|
11857
|
+
if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
|
|
11858
|
+
if (!(unwrappedExpression.operator === "||" && blockedExpressionValue || unwrappedExpression.operator === "&&" && !blockedExpressionValue)) return [];
|
|
11859
|
+
return [...collectBlockingBooleanStates(unwrappedExpression.left, blockedExpressionValue, context), ...collectBlockingBooleanStates(unwrappedExpression.right, blockedExpressionValue, context)];
|
|
11860
|
+
}
|
|
11861
|
+
if (isNodeOfType(unwrappedExpression, "BinaryExpression") && [
|
|
11862
|
+
"===",
|
|
11863
|
+
"==",
|
|
11864
|
+
"!==",
|
|
11865
|
+
"!="
|
|
11866
|
+
].includes(unwrappedExpression.operator)) {
|
|
11867
|
+
const leftValue = readStaticBoolean(unwrappedExpression.left);
|
|
11868
|
+
const rightValue = readStaticBoolean(unwrappedExpression.right);
|
|
11869
|
+
const booleanValue = leftValue ?? rightValue;
|
|
11870
|
+
const comparedKey = resolveExpressionKey(leftValue === null ? unwrappedExpression.left : unwrappedExpression.right, context);
|
|
11871
|
+
if (booleanValue === null || comparedKey === null) return [];
|
|
11872
|
+
return [{
|
|
11873
|
+
key: comparedKey,
|
|
11874
|
+
value: (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "==") === blockedExpressionValue ? booleanValue : !booleanValue
|
|
11875
|
+
}];
|
|
11876
|
+
}
|
|
11877
|
+
const expressionKey = resolveExpressionKey(unwrappedExpression, context);
|
|
11878
|
+
return expressionKey === null ? [] : [{
|
|
11879
|
+
key: expressionKey,
|
|
11880
|
+
value: blockedExpressionValue
|
|
11881
|
+
}];
|
|
11882
|
+
};
|
|
11883
|
+
const isDirectEarlyReturnConsequent = (ifStatement) => {
|
|
11884
|
+
if (!isNodeOfType(ifStatement, "IfStatement") || ifStatement.alternate) return false;
|
|
11885
|
+
if (isNodeOfType(ifStatement.consequent, "ReturnStatement")) return true;
|
|
11886
|
+
return isNodeOfType(ifStatement.consequent, "BlockStatement") && ifStatement.consequent.body.length === 1 && isNodeOfType(ifStatement.consequent.body[0], "ReturnStatement");
|
|
11887
|
+
};
|
|
11888
|
+
const collectDeferredUsageGuardStates = (callback, usageNode, context) => {
|
|
11889
|
+
if (!isFunctionLike$2(callback) || callback.async) return [];
|
|
11890
|
+
const guardStates = [];
|
|
11891
|
+
walkAst(callback.body, (child) => {
|
|
11892
|
+
if (child !== callback.body && isFunctionLike$2(child)) return false;
|
|
11893
|
+
if (isNodeOfType(child, "IfStatement") && isDirectEarlyReturnConsequent(child) && doMatchingNodesCoverEveryPathBeforeUsage(usageNode, [child], callback, context)) guardStates.push(...collectBlockingBooleanStates(child.test, true, context));
|
|
11894
|
+
});
|
|
11895
|
+
let descendant = usageNode;
|
|
11896
|
+
let ancestor = descendant.parent;
|
|
11897
|
+
while (ancestor && ancestor !== callback) {
|
|
11898
|
+
if (isNodeOfType(ancestor, "IfStatement") && ancestor.consequent === descendant) guardStates.push(...collectBlockingBooleanStates(ancestor.test, false, context));
|
|
11899
|
+
descendant = ancestor;
|
|
11900
|
+
ancestor = ancestor.parent;
|
|
11901
|
+
}
|
|
11902
|
+
return guardStates;
|
|
11903
|
+
};
|
|
11904
|
+
const cleanupReturnInvalidatesGuard = (cleanupReturn, guardState, context) => {
|
|
11905
|
+
if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return false;
|
|
11906
|
+
const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
|
|
11907
|
+
if (!cleanupFunction || !isFunctionLike$2(cleanupFunction) || cleanupFunction.async) return false;
|
|
11908
|
+
let didInvalidateGuard = false;
|
|
11909
|
+
walkAst(cleanupFunction.body, (child) => {
|
|
11910
|
+
if (didInvalidateGuard) return false;
|
|
11911
|
+
if (child !== cleanupFunction.body && isFunctionLike$2(child)) return false;
|
|
11912
|
+
if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && resolveExpressionKey(child.left, context) === guardState.key && readStaticBoolean(child.right) === guardState.value && context.cfg.isUnconditionalFromEntry(child)) {
|
|
11913
|
+
didInvalidateGuard = true;
|
|
11914
|
+
return false;
|
|
11915
|
+
}
|
|
11916
|
+
});
|
|
11917
|
+
return didInvalidateGuard;
|
|
11918
|
+
};
|
|
11919
|
+
const deferredUsageWritesGuardBeforeUsage = (callback, usageNode, guardState, context) => {
|
|
11920
|
+
const usageStart = getRangeStart(usageNode);
|
|
11921
|
+
if (!isFunctionLike$2(callback) || usageStart === null) return true;
|
|
11922
|
+
let didWriteGuard = false;
|
|
11923
|
+
walkAst(callback.body, (child) => {
|
|
11924
|
+
if (didWriteGuard) return false;
|
|
11925
|
+
if (child !== callback.body && isFunctionLike$2(child)) return false;
|
|
11926
|
+
const childStart = getRangeStart(child);
|
|
11927
|
+
if (childStart === null || childStart >= usageStart) return;
|
|
11928
|
+
const writtenExpression = isNodeOfType(child, "AssignmentExpression") ? child.left : isNodeOfType(child, "UpdateExpression") ? child.argument : null;
|
|
11929
|
+
if (writtenExpression && resolveExpressionKey(writtenExpression, context) === guardState.key) {
|
|
11930
|
+
didWriteGuard = true;
|
|
11931
|
+
return false;
|
|
11932
|
+
}
|
|
11933
|
+
});
|
|
11934
|
+
return didWriteGuard;
|
|
11935
|
+
};
|
|
11936
|
+
const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) => {
|
|
11937
|
+
const usageFunction = findEnclosingFunction(usage.node);
|
|
11938
|
+
const promiseChainCall = usageFunction ? getPromiseChainCallForCallback(usageFunction) : null;
|
|
11939
|
+
if (usage.handleKey === null || !usageFunction || usageFunction === callback || !promiseChainCall || !collectEffectInvokedFunctions(callback).has(usageFunction) || !doMatchingNodesCoverEveryPathAfterUsage(promiseChainCall, cleanupReturns, context)) return false;
|
|
11940
|
+
return collectDeferredUsageGuardStates(usageFunction, usage.node, context).some((guardState) => !deferredUsageWritesGuardBeforeUsage(usageFunction, usage.node, guardState, context) && cleanupReturns.every((cleanupReturn) => cleanupReturnInvalidatesGuard(cleanupReturn, guardState, context)));
|
|
11941
|
+
};
|
|
11847
11942
|
const effectHasCleanupForUsage = (callback, usage, context) => {
|
|
11848
11943
|
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
|
|
11849
11944
|
if (callback.async) return false;
|
|
@@ -11883,6 +11978,7 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
|
|
|
11883
11978
|
if (!cleanupFunction || !isFunctionLike$2(cleanupFunction)) return;
|
|
11884
11979
|
if (doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context)) matchingCleanupReturns.push(child);
|
|
11885
11980
|
});
|
|
11981
|
+
if (hasGuardedDeferredCleanup(callback, usage, matchingCleanupReturns, context)) return true;
|
|
11886
11982
|
return doMatchingNodesCoverEveryPathAfterUsage(resolveCleanupPathAnchor(usage.node, callback, context), matchingCleanupReturns, context);
|
|
11887
11983
|
};
|
|
11888
11984
|
const findFirstUsageWithoutCleanup = (callback, usages, context) => {
|
|
@@ -18333,6 +18429,26 @@ const MEMBERSHIP_COMPARISON_OPERATORS = new Set([
|
|
|
18333
18429
|
]);
|
|
18334
18430
|
const isNegativeOneLiteral = (expression) => Boolean(expression) && isNodeOfType(expression, "UnaryExpression") && expression.operator === "-" && isNodeOfType(expression.argument, "Literal") && expression.argument.value === 1;
|
|
18335
18431
|
const isZeroLiteral = (expression) => Boolean(expression) && isNodeOfType(expression, "Literal") && expression.value === 0;
|
|
18432
|
+
const isToIntegerOrInfinityZero = (value) => Number.isNaN(value) || Math.trunc(value) === 0;
|
|
18433
|
+
const isZeroFromIndex = (expression) => {
|
|
18434
|
+
if (!expression) return false;
|
|
18435
|
+
const strippedExpression = stripParenExpression(expression);
|
|
18436
|
+
if (isNodeOfType(strippedExpression, "Literal")) {
|
|
18437
|
+
if (typeof strippedExpression.value === "number") return isToIntegerOrInfinityZero(strippedExpression.value);
|
|
18438
|
+
if (typeof strippedExpression.value === "string") return isToIntegerOrInfinityZero(Number(strippedExpression.value));
|
|
18439
|
+
return strippedExpression.value === null || strippedExpression.value === false;
|
|
18440
|
+
}
|
|
18441
|
+
if (isNodeOfType(strippedExpression, "UnaryExpression") && strippedExpression.operator === "void") return true;
|
|
18442
|
+
if (isNodeOfType(strippedExpression, "UnaryExpression") && (strippedExpression.operator === "-" || strippedExpression.operator === "+") && isNodeOfType(strippedExpression.argument, "Literal") && typeof strippedExpression.argument.value === "number") return isToIntegerOrInfinityZero(strippedExpression.operator === "-" ? -strippedExpression.argument.value : strippedExpression.argument.value);
|
|
18443
|
+
if (isNodeOfType(strippedExpression, "Identifier")) return (strippedExpression.name === "undefined" || strippedExpression.name === "NaN") && findVariableInitializer(strippedExpression, strippedExpression.name) === null;
|
|
18444
|
+
return isNodeOfType(strippedExpression, "MemberExpression") && !strippedExpression.computed && isNodeOfType(strippedExpression.object, "Identifier") && strippedExpression.object.name === "Number" && isNodeOfType(strippedExpression.property, "Identifier") && strippedExpression.property.name === "NaN" && findVariableInitializer(strippedExpression.object, strippedExpression.object.name) === null;
|
|
18445
|
+
};
|
|
18446
|
+
const hasSemanticsPreservingIncludesArguments = (node) => {
|
|
18447
|
+
if (node.arguments.length === 1) return !isNodeOfType(node.arguments[0], "SpreadElement");
|
|
18448
|
+
if (node.arguments.length !== 2) return false;
|
|
18449
|
+
if (isNodeOfType(node.arguments[0], "SpreadElement") || isNodeOfType(node.arguments[1], "SpreadElement")) return false;
|
|
18450
|
+
return isZeroFromIndex(node.arguments[1]);
|
|
18451
|
+
};
|
|
18336
18452
|
const PARENT_WRAPPER_TYPES = new Set([
|
|
18337
18453
|
"ParenthesizedExpression",
|
|
18338
18454
|
"ChainExpression",
|
|
@@ -18353,6 +18469,306 @@ const isIndexOfResultUsedAsMembershipTest = (node) => {
|
|
|
18353
18469
|
if (isNegativeOneLiteral(otherOperand)) return true;
|
|
18354
18470
|
return isZeroLiteral(otherOperand) && (parent.operator === ">=" || parent.operator === "<");
|
|
18355
18471
|
};
|
|
18472
|
+
const getTypeAnnotation = (node) => {
|
|
18473
|
+
if (!node || !("typeAnnotation" in node)) return null;
|
|
18474
|
+
const annotation = node.typeAnnotation;
|
|
18475
|
+
if (!annotation || !isNodeOfType(annotation, "TSTypeAnnotation")) return null;
|
|
18476
|
+
return annotation.typeAnnotation;
|
|
18477
|
+
};
|
|
18478
|
+
const getDeclaredPropertyType = (members, propertyName) => {
|
|
18479
|
+
for (const member of members) if (isNodeOfType(member, "TSPropertySignature") && isNodeOfType(member.key, "Identifier") && member.key.name === propertyName) return getTypeAnnotation(member);
|
|
18480
|
+
return null;
|
|
18481
|
+
};
|
|
18482
|
+
const getArrayElementType = (typeNode) => {
|
|
18483
|
+
if (!typeNode) return null;
|
|
18484
|
+
if (isNodeOfType(typeNode, "TSArrayType")) return typeNode.elementType;
|
|
18485
|
+
if (isNodeOfType(typeNode, "TSTypeReference") && isNodeOfType(typeNode.typeName, "Identifier") && (typeNode.typeName.name === "Array" || typeNode.typeName.name === "ReadonlyArray")) return typeNode.typeArguments?.params?.[0] ?? null;
|
|
18486
|
+
if (isNodeOfType(typeNode, "TSUnionType")) {
|
|
18487
|
+
const arrayElementTypes = typeNode.types.map(getArrayElementType).filter(Boolean);
|
|
18488
|
+
return arrayElementTypes.length === 1 ? arrayElementTypes[0] : null;
|
|
18489
|
+
}
|
|
18490
|
+
return null;
|
|
18491
|
+
};
|
|
18492
|
+
const getDestructuredDeclaredType = (identifier) => {
|
|
18493
|
+
const binding = findVariableInitializer(identifier, identifier.name);
|
|
18494
|
+
if (!binding) return null;
|
|
18495
|
+
const property = binding.bindingIdentifier.parent;
|
|
18496
|
+
const objectPattern = property?.parent;
|
|
18497
|
+
if (!isNodeOfType(property, "Property") || !isNodeOfType(property.key, "Identifier") || !isNodeOfType(objectPattern, "ObjectPattern")) return null;
|
|
18498
|
+
const propsType = getTypeAnnotation(objectPattern);
|
|
18499
|
+
if (!propsType) return null;
|
|
18500
|
+
if (isNodeOfType(propsType, "TSTypeLiteral")) return getDeclaredPropertyType(propsType.members ?? [], property.key.name);
|
|
18501
|
+
if (!isNodeOfType(propsType, "TSTypeReference") || !isNodeOfType(propsType.typeName, "Identifier")) return null;
|
|
18502
|
+
const program = findProgramRoot(identifier);
|
|
18503
|
+
if (!program) return null;
|
|
18504
|
+
for (const statement of program.body) {
|
|
18505
|
+
const declaration = isNodeOfType(statement, "ExportNamedDeclaration") ? statement.declaration : statement;
|
|
18506
|
+
if (isNodeOfType(declaration, "TSInterfaceDeclaration") && isNodeOfType(declaration.id, "Identifier") && declaration.id.name === propsType.typeName.name) return getDeclaredPropertyType(declaration.body.body, property.key.name);
|
|
18507
|
+
if (isNodeOfType(declaration, "TSTypeAliasDeclaration") && isNodeOfType(declaration.id, "Identifier") && declaration.id.name === propsType.typeName.name && isNodeOfType(declaration.typeAnnotation, "TSTypeLiteral")) return getDeclaredPropertyType(declaration.typeAnnotation.members ?? [], property.key.name);
|
|
18508
|
+
}
|
|
18509
|
+
return null;
|
|
18510
|
+
};
|
|
18511
|
+
const getIdentifierDeclaredType = (identifier, visitedBindingIdentifiers = /* @__PURE__ */ new Set()) => {
|
|
18512
|
+
const binding = findVariableInitializer(identifier, identifier.name);
|
|
18513
|
+
if (!binding || visitedBindingIdentifiers.has(binding.bindingIdentifier)) return null;
|
|
18514
|
+
visitedBindingIdentifiers.add(binding.bindingIdentifier);
|
|
18515
|
+
const directType = getTypeAnnotation(binding.bindingIdentifier);
|
|
18516
|
+
if (directType) return directType;
|
|
18517
|
+
const initializer = binding.initializer;
|
|
18518
|
+
if (isNodeOfType(initializer, "TSAsExpression") || isNodeOfType(initializer, "TSTypeAssertion") || isNodeOfType(initializer, "TSSatisfiesExpression")) return initializer.typeAnnotation;
|
|
18519
|
+
const destructuredType = getDestructuredDeclaredType(identifier);
|
|
18520
|
+
if (destructuredType) return destructuredType;
|
|
18521
|
+
const declarator = binding.bindingIdentifier.parent;
|
|
18522
|
+
const declaration = declarator?.parent;
|
|
18523
|
+
const forOfStatement = declaration?.parent;
|
|
18524
|
+
if (isNodeOfType(declarator, "VariableDeclarator") && isNodeOfType(declaration, "VariableDeclaration") && isNodeOfType(forOfStatement, "ForOfStatement") && forOfStatement.left === declaration && isNodeOfType(forOfStatement.right, "Identifier")) return getArrayElementType(getIdentifierDeclaredType(forOfStatement.right, visitedBindingIdentifiers));
|
|
18525
|
+
return null;
|
|
18526
|
+
};
|
|
18527
|
+
const isNativeIterationIndex = (identifier) => {
|
|
18528
|
+
const binding = findVariableInitializer(identifier, identifier.name);
|
|
18529
|
+
if (!binding) return false;
|
|
18530
|
+
const callback = binding.bindingIdentifier.parent;
|
|
18531
|
+
if (!isInlineFunctionExpression(callback)) return false;
|
|
18532
|
+
const callbackCall = callback.parent;
|
|
18533
|
+
if (!isNodeOfType(callbackCall, "CallExpression") || !isIterationCallbackCall(callbackCall) || !isNodeOfType(callbackCall.callee, "MemberExpression") || !isNodeOfType(callbackCall.callee.property, "Identifier")) return false;
|
|
18534
|
+
const indexParameterPosition = callbackCall.callee.property.name === "reduce" || callbackCall.callee.property.name === "reduceRight" ? 2 : 1;
|
|
18535
|
+
return callback.params?.[indexParameterPosition] === binding.bindingIdentifier;
|
|
18536
|
+
};
|
|
18537
|
+
const hasSameIdentifierBinding = (leftIdentifier, rightIdentifier) => {
|
|
18538
|
+
if (leftIdentifier.name !== rightIdentifier.name) return false;
|
|
18539
|
+
const leftBinding = findVariableInitializer(leftIdentifier, leftIdentifier.name);
|
|
18540
|
+
const rightBinding = findVariableInitializer(rightIdentifier, rightIdentifier.name);
|
|
18541
|
+
return Boolean(leftBinding && rightBinding && leftBinding.bindingIdentifier === rightBinding.bindingIdentifier);
|
|
18542
|
+
};
|
|
18543
|
+
const NON_NAN_RELATIONAL_OPERATORS = new Set([
|
|
18544
|
+
"<",
|
|
18545
|
+
"<=",
|
|
18546
|
+
">",
|
|
18547
|
+
">="
|
|
18548
|
+
]);
|
|
18549
|
+
const testProvesIdentifierIsNotNaN = (test, identifier) => {
|
|
18550
|
+
if (!test) return false;
|
|
18551
|
+
const strippedTest = stripParenExpression(test);
|
|
18552
|
+
if (isNodeOfType(strippedTest, "LogicalExpression") && strippedTest.operator === "&&") return testProvesIdentifierIsNotNaN(strippedTest.left, identifier) || testProvesIdentifierIsNotNaN(strippedTest.right, identifier);
|
|
18553
|
+
if (!isNodeOfType(strippedTest, "BinaryExpression") || !NON_NAN_RELATIONAL_OPERATORS.has(strippedTest.operator)) return false;
|
|
18554
|
+
return isNodeOfType(strippedTest.left, "Identifier") && hasSameIdentifierBinding(strippedTest.left, identifier) || isNodeOfType(strippedTest.right, "Identifier") && hasSameIdentifierBinding(strippedTest.right, identifier);
|
|
18555
|
+
};
|
|
18556
|
+
const writeTargetContainsIdentifierBinding = (writeTarget, identifier) => {
|
|
18557
|
+
let containsBinding = false;
|
|
18558
|
+
walkAst(writeTarget, (child) => {
|
|
18559
|
+
if (isNodeOfType(child, "Identifier") && hasSameIdentifierBinding(child, identifier)) {
|
|
18560
|
+
containsBinding = true;
|
|
18561
|
+
return false;
|
|
18562
|
+
}
|
|
18563
|
+
});
|
|
18564
|
+
return containsBinding;
|
|
18565
|
+
};
|
|
18566
|
+
const hasWriteBeforeQuery = (body, identifier) => {
|
|
18567
|
+
const queryStart = getRangeStart(identifier);
|
|
18568
|
+
if (queryStart === null) return true;
|
|
18569
|
+
let hasEarlierWrite = false;
|
|
18570
|
+
walkAst(body, (child) => {
|
|
18571
|
+
if (hasEarlierWrite) return false;
|
|
18572
|
+
const childStart = getRangeStart(child);
|
|
18573
|
+
if (childStart !== null && childStart >= queryStart) return false;
|
|
18574
|
+
if (child !== body && isFunctionLike$2(child)) return false;
|
|
18575
|
+
const writeTarget = isNodeOfType(child, "AssignmentExpression") ? child.left : isNodeOfType(child, "UpdateExpression") ? child.argument : isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement") ? child.left : null;
|
|
18576
|
+
if (writeTarget && writeTargetContainsIdentifierBinding(writeTarget, identifier)) {
|
|
18577
|
+
hasEarlierWrite = true;
|
|
18578
|
+
return false;
|
|
18579
|
+
}
|
|
18580
|
+
});
|
|
18581
|
+
return hasEarlierWrite;
|
|
18582
|
+
};
|
|
18583
|
+
const isProtectedByRelationalLoopGuard = (identifier) => {
|
|
18584
|
+
let descendant = identifier;
|
|
18585
|
+
let ancestor = identifier.parent;
|
|
18586
|
+
while (ancestor) {
|
|
18587
|
+
if (isFunctionLike$2(ancestor)) return false;
|
|
18588
|
+
if ((isNodeOfType(ancestor, "ForStatement") || isNodeOfType(ancestor, "WhileStatement")) && ancestor.body === descendant && testProvesIdentifierIsNotNaN(ancestor.test, identifier)) return !hasWriteBeforeQuery(ancestor.body, identifier);
|
|
18589
|
+
descendant = ancestor;
|
|
18590
|
+
ancestor = ancestor.parent;
|
|
18591
|
+
}
|
|
18592
|
+
return false;
|
|
18593
|
+
};
|
|
18594
|
+
const isKnownSafeIndexOfQuery = (query) => {
|
|
18595
|
+
if (!query) return false;
|
|
18596
|
+
const strippedQuery = stripParenExpression(query);
|
|
18597
|
+
if (isNodeOfType(strippedQuery, "Literal")) return typeof strippedQuery.value !== "number" || Number.isFinite(strippedQuery.value);
|
|
18598
|
+
if (!isNodeOfType(strippedQuery, "Identifier")) return false;
|
|
18599
|
+
if (isNativeIterationIndex(strippedQuery)) return true;
|
|
18600
|
+
return isProtectedByRelationalLoopGuard(strippedQuery);
|
|
18601
|
+
};
|
|
18602
|
+
const findSameFileTypeAlias = (reference, typeName) => {
|
|
18603
|
+
const program = findProgramRoot(reference);
|
|
18604
|
+
if (!program) return null;
|
|
18605
|
+
for (const statement of program.body) {
|
|
18606
|
+
const declaration = isNodeOfType(statement, "ExportNamedDeclaration") ? statement.declaration : statement;
|
|
18607
|
+
if (declaration && isNodeOfType(declaration, "TSTypeAliasDeclaration") && isNodeOfType(declaration.id, "Identifier") && declaration.id.name === typeName) return declaration;
|
|
18608
|
+
}
|
|
18609
|
+
return null;
|
|
18610
|
+
};
|
|
18611
|
+
const hasDeclaredMembershipMethod = (members, methodName) => members.some((member) => (isNodeOfType(member, "TSMethodSignature") || isNodeOfType(member, "TSPropertySignature")) && isNodeOfType(member.key, "Identifier") && member.key.name === methodName);
|
|
18612
|
+
const isKnownUserlandMembershipReceiver = (receiver, methodName) => {
|
|
18613
|
+
if (!isNodeOfType(receiver, "Identifier")) return false;
|
|
18614
|
+
const declaredType = getIdentifierDeclaredType(receiver);
|
|
18615
|
+
if (!declaredType) return false;
|
|
18616
|
+
if (isNodeOfType(declaredType, "TSTypeLiteral")) return hasDeclaredMembershipMethod(declaredType.members ?? [], methodName);
|
|
18617
|
+
if (!isNodeOfType(declaredType, "TSTypeReference") || !isNodeOfType(declaredType.typeName, "Identifier")) return false;
|
|
18618
|
+
const typeAlias = findSameFileTypeAlias(receiver, declaredType.typeName.name);
|
|
18619
|
+
if (typeAlias && isNodeOfType(typeAlias.typeAnnotation, "TSTypeLiteral")) return hasDeclaredMembershipMethod(typeAlias.typeAnnotation.members ?? [], methodName);
|
|
18620
|
+
const program = findProgramRoot(receiver);
|
|
18621
|
+
if (!program) return false;
|
|
18622
|
+
for (const statement of program.body) {
|
|
18623
|
+
const declaration = isNodeOfType(statement, "ExportNamedDeclaration") ? statement.declaration : statement;
|
|
18624
|
+
if (declaration && isNodeOfType(declaration, "TSInterfaceDeclaration") && isNodeOfType(declaration.id, "Identifier") && declaration.id.name === declaredType.typeName.name) return hasDeclaredMembershipMethod(declaration.body.body, methodName);
|
|
18625
|
+
}
|
|
18626
|
+
return false;
|
|
18627
|
+
};
|
|
18628
|
+
const findTypeParameter = (reference, typeName) => {
|
|
18629
|
+
let ancestor = reference.parent;
|
|
18630
|
+
while (ancestor) {
|
|
18631
|
+
if (isFunctionLike$2(ancestor) || isNodeOfType(ancestor, "ClassDeclaration") || isNodeOfType(ancestor, "ClassExpression")) {
|
|
18632
|
+
const matchingTypeParameter = ancestor.typeParameters?.params?.find((typeParameter) => isNodeOfType(typeParameter, "TSTypeParameter") && isNodeOfType(typeParameter.name, "Identifier") && typeParameter.name.name === typeName);
|
|
18633
|
+
if (matchingTypeParameter && isNodeOfType(matchingTypeParameter, "TSTypeParameter")) return matchingTypeParameter;
|
|
18634
|
+
}
|
|
18635
|
+
ancestor = ancestor.parent;
|
|
18636
|
+
}
|
|
18637
|
+
return null;
|
|
18638
|
+
};
|
|
18639
|
+
const POSSIBLY_NUMERIC_TYPE_REFERENCE_NAMES = new Set(["NonNullable", "PropertyKey"]);
|
|
18640
|
+
const buildTypeAliasArguments = (typeAlias, typeReference, inheritedArguments) => {
|
|
18641
|
+
const typeArguments = new Map(inheritedArguments);
|
|
18642
|
+
for (const [index, typeParameter] of (typeAlias.typeParameters?.params ?? []).entries()) {
|
|
18643
|
+
if (!isNodeOfType(typeParameter, "TSTypeParameter")) continue;
|
|
18644
|
+
if (!isNodeOfType(typeParameter.name, "Identifier")) continue;
|
|
18645
|
+
const argument = typeReference.typeArguments?.params?.[index] ?? typeParameter.default;
|
|
18646
|
+
if (argument) typeArguments.set(typeParameter.name.name, argument);
|
|
18647
|
+
}
|
|
18648
|
+
return typeArguments;
|
|
18649
|
+
};
|
|
18650
|
+
const typeCanHaveSameValueZeroDifference = (typeNode, reference, activeTypeNodes, typeArguments = /* @__PURE__ */ new Map()) => {
|
|
18651
|
+
if (!typeNode) return false;
|
|
18652
|
+
if (isNodeOfType(typeNode, "TSNumberKeyword") || isNodeOfType(typeNode, "TSAnyKeyword") || isNodeOfType(typeNode, "TSUnknownKeyword")) return true;
|
|
18653
|
+
if (isNodeOfType(typeNode, "TSStringKeyword") || isNodeOfType(typeNode, "TSBooleanKeyword") || isNodeOfType(typeNode, "TSBigIntKeyword") || isNodeOfType(typeNode, "TSSymbolKeyword") || isNodeOfType(typeNode, "TSNullKeyword") || isNodeOfType(typeNode, "TSUndefinedKeyword") || isNodeOfType(typeNode, "TSObjectKeyword") || isNodeOfType(typeNode, "TSLiteralType") || isNodeOfType(typeNode, "TSFunctionType")) return false;
|
|
18654
|
+
if (isNodeOfType(typeNode, "TSTypeLiteral")) return (typeNode.members?.length ?? 0) === 0;
|
|
18655
|
+
if (isNodeOfType(typeNode, "TSTypeOperator") && typeNode.operator === "keyof") return false;
|
|
18656
|
+
if (isNodeOfType(typeNode, "TSUnionType") || isNodeOfType(typeNode, "TSIntersectionType")) return typeNode.types.some((memberType) => typeCanHaveSameValueZeroDifference(memberType, reference, activeTypeNodes, typeArguments));
|
|
18657
|
+
if (isNodeOfType(typeNode, "TSOptionalType") || isNodeOfType(typeNode, "TSRestType")) return typeCanHaveSameValueZeroDifference(typeNode.typeAnnotation, reference, activeTypeNodes, typeArguments);
|
|
18658
|
+
if (isNodeOfType(typeNode, "TSNamedTupleMember")) return typeCanHaveSameValueZeroDifference(typeNode.elementType, reference, activeTypeNodes, typeArguments);
|
|
18659
|
+
if (!isNodeOfType(typeNode, "TSTypeReference")) return true;
|
|
18660
|
+
if (!isNodeOfType(typeNode.typeName, "Identifier")) return false;
|
|
18661
|
+
const substitutedType = typeArguments.get(typeNode.typeName.name);
|
|
18662
|
+
if (substitutedType) {
|
|
18663
|
+
const remainingArguments = new Map(typeArguments);
|
|
18664
|
+
remainingArguments.delete(typeNode.typeName.name);
|
|
18665
|
+
return typeCanHaveSameValueZeroDifference(substitutedType, reference, activeTypeNodes, remainingArguments);
|
|
18666
|
+
}
|
|
18667
|
+
const typeParameter = findTypeParameter(reference, typeNode.typeName.name);
|
|
18668
|
+
if (typeParameter) {
|
|
18669
|
+
if (!typeParameter.constraint || activeTypeNodes.has(typeParameter)) return true;
|
|
18670
|
+
activeTypeNodes.add(typeParameter);
|
|
18671
|
+
const constraintCanDiffer = typeCanHaveSameValueZeroDifference(typeParameter.constraint, reference, activeTypeNodes, typeArguments);
|
|
18672
|
+
activeTypeNodes.delete(typeParameter);
|
|
18673
|
+
return constraintCanDiffer;
|
|
18674
|
+
}
|
|
18675
|
+
const typeAlias = findSameFileTypeAlias(reference, typeNode.typeName.name);
|
|
18676
|
+
if (!typeAlias) return POSSIBLY_NUMERIC_TYPE_REFERENCE_NAMES.has(typeNode.typeName.name) || /(?:number|numeric)/i.test(typeNode.typeName.name);
|
|
18677
|
+
if (activeTypeNodes.has(typeAlias)) return true;
|
|
18678
|
+
activeTypeNodes.add(typeAlias);
|
|
18679
|
+
const canDiffer = typeCanHaveSameValueZeroDifference(typeAlias.typeAnnotation, reference, activeTypeNodes, buildTypeAliasArguments(typeAlias, typeNode, typeArguments));
|
|
18680
|
+
activeTypeNodes.delete(typeAlias);
|
|
18681
|
+
return canDiffer;
|
|
18682
|
+
};
|
|
18683
|
+
const arrayTypeCanHaveSameValueZeroDifference = (typeNode, reference, activeTypeNodes, typeArguments = /* @__PURE__ */ new Map()) => {
|
|
18684
|
+
if (!typeNode) return false;
|
|
18685
|
+
if (isNodeOfType(typeNode, "TSArrayType")) return typeCanHaveSameValueZeroDifference(typeNode.elementType, reference, activeTypeNodes, typeArguments);
|
|
18686
|
+
if (isNodeOfType(typeNode, "TSTupleType")) return typeNode.elementTypes.some((elementType) => {
|
|
18687
|
+
if (isNodeOfType(elementType, "TSRestType")) return arrayTypeCanHaveSameValueZeroDifference(elementType.typeAnnotation, reference, activeTypeNodes, typeArguments);
|
|
18688
|
+
if (isNodeOfType(elementType, "TSNamedTupleMember")) return typeCanHaveSameValueZeroDifference(elementType.elementType, reference, activeTypeNodes, typeArguments);
|
|
18689
|
+
return typeCanHaveSameValueZeroDifference(elementType, reference, activeTypeNodes, typeArguments);
|
|
18690
|
+
});
|
|
18691
|
+
if (isNodeOfType(typeNode, "TSTypeOperator")) return arrayTypeCanHaveSameValueZeroDifference(typeNode.typeAnnotation ?? null, reference, activeTypeNodes, typeArguments);
|
|
18692
|
+
if (isNodeOfType(typeNode, "TSUnionType") || isNodeOfType(typeNode, "TSIntersectionType")) return typeNode.types.some((memberType) => arrayTypeCanHaveSameValueZeroDifference(memberType, reference, activeTypeNodes, typeArguments));
|
|
18693
|
+
if (!isNodeOfType(typeNode, "TSTypeReference") || !isNodeOfType(typeNode.typeName, "Identifier")) return false;
|
|
18694
|
+
const substitutedType = typeArguments.get(typeNode.typeName.name);
|
|
18695
|
+
if (substitutedType) {
|
|
18696
|
+
const remainingArguments = new Map(typeArguments);
|
|
18697
|
+
remainingArguments.delete(typeNode.typeName.name);
|
|
18698
|
+
return arrayTypeCanHaveSameValueZeroDifference(substitutedType, reference, activeTypeNodes, remainingArguments);
|
|
18699
|
+
}
|
|
18700
|
+
if (typeNode.typeName.name === "Array" || typeNode.typeName.name === "ReadonlyArray") return typeCanHaveSameValueZeroDifference(typeNode.typeArguments?.params?.[0] ?? null, reference, activeTypeNodes, typeArguments);
|
|
18701
|
+
const typeAlias = findSameFileTypeAlias(reference, typeNode.typeName.name);
|
|
18702
|
+
if (!typeAlias || activeTypeNodes.has(typeAlias)) return false;
|
|
18703
|
+
activeTypeNodes.add(typeAlias);
|
|
18704
|
+
const canDiffer = arrayTypeCanHaveSameValueZeroDifference(typeAlias.typeAnnotation, reference, activeTypeNodes, buildTypeAliasArguments(typeAlias, typeNode, typeArguments));
|
|
18705
|
+
activeTypeNodes.delete(typeAlias);
|
|
18706
|
+
return canDiffer;
|
|
18707
|
+
};
|
|
18708
|
+
const isKnownArrayType = (typeNode, reference, activeTypeNodes, typeArguments = /* @__PURE__ */ new Map()) => {
|
|
18709
|
+
if (!typeNode) return false;
|
|
18710
|
+
if (isNodeOfType(typeNode, "TSArrayType") || isNodeOfType(typeNode, "TSTupleType")) return true;
|
|
18711
|
+
if (isNodeOfType(typeNode, "TSTypeOperator")) return isKnownArrayType(typeNode.typeAnnotation ?? null, reference, activeTypeNodes, typeArguments);
|
|
18712
|
+
if (isNodeOfType(typeNode, "TSUnionType")) {
|
|
18713
|
+
const nonNullishTypes = typeNode.types.filter((memberType) => !isNodeOfType(memberType, "TSNullKeyword") && !isNodeOfType(memberType, "TSUndefinedKeyword"));
|
|
18714
|
+
return nonNullishTypes.length > 0 && nonNullishTypes.every((memberType) => isKnownArrayType(memberType, reference, activeTypeNodes, typeArguments));
|
|
18715
|
+
}
|
|
18716
|
+
if (isNodeOfType(typeNode, "TSIntersectionType")) return typeNode.types.some((memberType) => isKnownArrayType(memberType, reference, activeTypeNodes, typeArguments));
|
|
18717
|
+
if (!isNodeOfType(typeNode, "TSTypeReference") || !isNodeOfType(typeNode.typeName, "Identifier")) return false;
|
|
18718
|
+
const substitutedType = typeArguments.get(typeNode.typeName.name);
|
|
18719
|
+
if (substitutedType) {
|
|
18720
|
+
const remainingArguments = new Map(typeArguments);
|
|
18721
|
+
remainingArguments.delete(typeNode.typeName.name);
|
|
18722
|
+
return isKnownArrayType(substitutedType, reference, activeTypeNodes, remainingArguments);
|
|
18723
|
+
}
|
|
18724
|
+
if (typeNode.typeName.name === "Array" || typeNode.typeName.name === "ReadonlyArray") return true;
|
|
18725
|
+
const typeParameter = findTypeParameter(reference, typeNode.typeName.name);
|
|
18726
|
+
if (typeParameter?.constraint) {
|
|
18727
|
+
if (activeTypeNodes.has(typeParameter)) return false;
|
|
18728
|
+
activeTypeNodes.add(typeParameter);
|
|
18729
|
+
const isConstraintArray = isKnownArrayType(typeParameter.constraint, reference, activeTypeNodes, typeArguments);
|
|
18730
|
+
activeTypeNodes.delete(typeParameter);
|
|
18731
|
+
return isConstraintArray;
|
|
18732
|
+
}
|
|
18733
|
+
const typeAlias = findSameFileTypeAlias(reference, typeNode.typeName.name);
|
|
18734
|
+
if (!typeAlias || activeTypeNodes.has(typeAlias)) return false;
|
|
18735
|
+
activeTypeNodes.add(typeAlias);
|
|
18736
|
+
const isArray = isKnownArrayType(typeAlias.typeAnnotation, reference, activeTypeNodes, buildTypeAliasArguments(typeAlias, typeNode, typeArguments));
|
|
18737
|
+
activeTypeNodes.delete(typeAlias);
|
|
18738
|
+
return isArray;
|
|
18739
|
+
};
|
|
18740
|
+
const isKnownNativeArrayReceiver = (receiver) => {
|
|
18741
|
+
if (isNodeOfType(receiver, "ArrayExpression")) return true;
|
|
18742
|
+
if (isNodeOfType(receiver, "Identifier") && isKnownArrayType(getIdentifierDeclaredType(receiver), receiver, /* @__PURE__ */ new Set())) return true;
|
|
18743
|
+
const initializer = getResolvedInitializer(receiver)?.initializer;
|
|
18744
|
+
if (!initializer) return false;
|
|
18745
|
+
const strippedInitializer = stripParenExpression(initializer);
|
|
18746
|
+
if (isNodeOfType(strippedInitializer, "ArrayExpression")) return true;
|
|
18747
|
+
if (isNodeOfType(strippedInitializer, "NewExpression") && isNodeOfType(strippedInitializer.callee, "Identifier") && strippedInitializer.callee.name === "Array" && findVariableInitializer(strippedInitializer.callee, strippedInitializer.callee.name) === null) return true;
|
|
18748
|
+
return isNodeOfType(strippedInitializer, "CallExpression") && isNodeOfType(strippedInitializer.callee, "MemberExpression") && isNodeOfType(strippedInitializer.callee.object, "Identifier") && strippedInitializer.callee.object.name === "Array" && isNodeOfType(strippedInitializer.callee.property, "Identifier") && (strippedInitializer.callee.property.name === "from" || strippedInitializer.callee.property.name === "of") && findVariableInitializer(strippedInitializer.callee.object, strippedInitializer.callee.object.name) === null;
|
|
18749
|
+
};
|
|
18750
|
+
const isKnownDenseArrayReceiver = (receiver) => {
|
|
18751
|
+
const initializer = isNodeOfType(receiver, "Identifier") ? getResolvedInitializer(receiver)?.initializer : receiver;
|
|
18752
|
+
if (!initializer) return false;
|
|
18753
|
+
const strippedInitializer = stripParenExpression(initializer);
|
|
18754
|
+
if (isNodeOfType(strippedInitializer, "ArrayExpression")) return (strippedInitializer.elements ?? []).every((element) => element !== null && !isNodeOfType(element, "SpreadElement"));
|
|
18755
|
+
return isNodeOfType(strippedInitializer, "CallExpression") && isNodeOfType(strippedInitializer.callee, "MemberExpression") && isNodeOfType(strippedInitializer.callee.object, "Identifier") && strippedInitializer.callee.object.name === "Array" && isNodeOfType(strippedInitializer.callee.property, "Identifier") && (strippedInitializer.callee.property.name === "from" || strippedInitializer.callee.property.name === "of") && findVariableInitializer(strippedInitializer.callee.object, strippedInitializer.callee.object.name) === null;
|
|
18756
|
+
};
|
|
18757
|
+
const isKnownUnsafeIndexOfQuery = (query, receiver) => {
|
|
18758
|
+
if (!query) return true;
|
|
18759
|
+
const strippedQuery = stripParenExpression(query);
|
|
18760
|
+
if (isNodeOfType(strippedQuery, "Identifier")) {
|
|
18761
|
+
if (strippedQuery.name === "undefined" && findVariableInitializer(strippedQuery, strippedQuery.name) === null) return !isKnownDenseArrayReceiver(receiver);
|
|
18762
|
+
if (strippedQuery.name === "NaN" && findVariableInitializer(strippedQuery, strippedQuery.name) === null) return true;
|
|
18763
|
+
return typeCanHaveSameValueZeroDifference(getIdentifierDeclaredType(strippedQuery), strippedQuery, /* @__PURE__ */ new Set());
|
|
18764
|
+
}
|
|
18765
|
+
if (isNodeOfType(strippedQuery, "MemberExpression")) return !strippedQuery.computed && isNodeOfType(strippedQuery.object, "Identifier") && strippedQuery.object.name === "Number" && isNodeOfType(strippedQuery.property, "Identifier") && strippedQuery.property.name === "NaN" && findVariableInitializer(strippedQuery.object, strippedQuery.object.name) === null;
|
|
18766
|
+
return true;
|
|
18767
|
+
};
|
|
18768
|
+
const isKnownUnsafeIndexOfReceiver = (receiver) => {
|
|
18769
|
+
if (!isNodeOfType(receiver, "Identifier")) return false;
|
|
18770
|
+
return arrayTypeCanHaveSameValueZeroDifference(getIdentifierDeclaredType(receiver), receiver, /* @__PURE__ */ new Set());
|
|
18771
|
+
};
|
|
18356
18772
|
const ITERATION_CALLBACK_METHOD_NAMES = new Set([
|
|
18357
18773
|
"forEach",
|
|
18358
18774
|
"map",
|
|
@@ -18481,16 +18897,22 @@ const jsSetMapLookups = defineRule({
|
|
|
18481
18897
|
if (!isNodeOfType(node.callee, "MemberExpression") || !isNodeOfType(node.callee.property, "Identifier")) return;
|
|
18482
18898
|
const methodName = node.callee.property.name;
|
|
18483
18899
|
if (methodName !== "includes" && methodName !== "indexOf") return;
|
|
18484
|
-
if (methodName === "
|
|
18900
|
+
if (methodName === "includes" && !hasSemanticsPreservingIncludesArguments(node)) return;
|
|
18901
|
+
if (methodName === "indexOf" && (node.arguments.length !== 1 || isNodeOfType(node.arguments[0], "SpreadElement") || !isIndexOfResultUsedAsMembershipTest(node))) return;
|
|
18485
18902
|
const rawReceiver = node.callee.object;
|
|
18486
18903
|
if (!rawReceiver) return;
|
|
18487
18904
|
const receiver = stripParenExpression(rawReceiver);
|
|
18905
|
+
const isKnownNativeArray = isKnownNativeArrayReceiver(receiver);
|
|
18906
|
+
if (isKnownUserlandMembershipReceiver(receiver, methodName)) return;
|
|
18907
|
+
if (methodName === "includes" && node.arguments.length === 2 && !isKnownNativeArray) return;
|
|
18908
|
+
const query = node.arguments[0];
|
|
18909
|
+
if (methodName === "indexOf" && !isKnownSafeIndexOfQuery(query) && (isKnownUnsafeIndexOfQuery(query, receiver) || isKnownUnsafeIndexOfReceiver(receiver))) return;
|
|
18488
18910
|
if (isLikelyStringReceiver(receiver)) return;
|
|
18489
18911
|
if (isSmallInlineLiteralArray(receiver)) return;
|
|
18490
18912
|
if (isScreamingSnakeCaseConstantReceiver(receiver)) return;
|
|
18491
18913
|
if (isSmallFixedListMember(receiver)) return;
|
|
18492
|
-
if (isSubstringSearchLiteral(
|
|
18493
|
-
if (isIndexedArrayElementWithStringArgument(receiver,
|
|
18914
|
+
if (isSubstringSearchLiteral(query)) return;
|
|
18915
|
+
if (isIndexedArrayElementWithStringArgument(receiver, query)) return;
|
|
18494
18916
|
const resolvedInitializer = getResolvedInitializer(receiver);
|
|
18495
18917
|
if (resolvedInitializer) {
|
|
18496
18918
|
if (isLikelyStringReceiver(resolvedInitializer.initializer)) return;
|
|
@@ -25123,8 +25545,11 @@ const unwrapUseCallback = (node) => {
|
|
|
25123
25545
|
const callee = node.callee;
|
|
25124
25546
|
return isNodeOfType(callee, "Identifier") && callee.name === "useCallback" || isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "useCallback" ? node.arguments?.[0] : node;
|
|
25125
25547
|
};
|
|
25548
|
+
const hasParameterDefinition = (ref) => Boolean(ref.resolved?.defs.some((definition) => definition.type === "Parameter"));
|
|
25126
25549
|
const resolveToFunction = (ref) => {
|
|
25127
|
-
const
|
|
25550
|
+
const definition = ref.resolved?.defs[0];
|
|
25551
|
+
if (!definition || hasParameterDefinition(ref)) return null;
|
|
25552
|
+
const definitionNode = definition.node;
|
|
25128
25553
|
if (!definitionNode) return null;
|
|
25129
25554
|
if (isFunctionLike$2(definitionNode)) return definitionNode;
|
|
25130
25555
|
if (isNodeOfType(definitionNode, "VariableDeclarator")) {
|
|
@@ -25534,7 +25959,7 @@ const isCleanupReturnArgument = (analysis, node) => {
|
|
|
25534
25959
|
if (isNodeOfType(node, "MemberExpression")) return true;
|
|
25535
25960
|
if (isNodeOfType(node, "Identifier")) {
|
|
25536
25961
|
const ref = getRef(analysis, node);
|
|
25537
|
-
if (ref && resolveToFunction(ref)) return true;
|
|
25962
|
+
if (ref && (hasParameterDefinition(ref) || resolveToFunction(ref))) return true;
|
|
25538
25963
|
}
|
|
25539
25964
|
if (isNodeOfType(node, "ConditionalExpression")) return isCleanupReturnArgument(analysis, node.consequent) || isCleanupReturnArgument(analysis, node.alternate);
|
|
25540
25965
|
return false;
|
|
@@ -26006,7 +26431,7 @@ const resolveWrappedCallable = (analysis, node) => {
|
|
|
26006
26431
|
if (isNodeOfType(candidate, "Identifier")) {
|
|
26007
26432
|
const reference = getRef(analysis, candidate);
|
|
26008
26433
|
if (!reference) return null;
|
|
26009
|
-
if (reference.resolved?.defs.some((definition) => definition.type === "
|
|
26434
|
+
if (reference.resolved?.defs.some((definition) => definition.type === "ImportBinding")) return null;
|
|
26010
26435
|
const resolved = resolveToFunction(reference);
|
|
26011
26436
|
if (resolved) return resolved;
|
|
26012
26437
|
const definitionNode = reference.resolved?.defs[0]?.node;
|
|
@@ -37815,8 +38240,6 @@ const getWrapperHookWrappedFunction = (initializer) => {
|
|
|
37815
38240
|
if (!wrapped || !isFunctionLike$2(wrapped)) return null;
|
|
37816
38241
|
return wrapped;
|
|
37817
38242
|
};
|
|
37818
|
-
const hasParameterDef = (ref) => Boolean(ref.resolved?.defs.some((def) => def.type === "Parameter"));
|
|
37819
|
-
const resolvesToFunctionBinding = (ref) => !hasParameterDef(ref) && Boolean(resolveToFunction(ref));
|
|
37820
38243
|
const HANDLER_NAMED_PROP_PATTERN = /^(on|handle)[A-Z]/;
|
|
37821
38244
|
const wrappedFunctionNotifiesParent = (analysis, wrappedFunction) => getDownstreamRefs(analysis, wrappedFunction).some((innerRef) => {
|
|
37822
38245
|
if (!isProp(analysis, innerRef)) return false;
|
|
@@ -38137,7 +38560,7 @@ const noPassDataToParent = defineRule({
|
|
|
38137
38560
|
if (isConstant(argRef)) return false;
|
|
38138
38561
|
if (isParentWiredHookResultRef(analysis, argRef)) return false;
|
|
38139
38562
|
if (isParentWiredHookCalleeRef(analysis, argRef)) return false;
|
|
38140
|
-
if (
|
|
38563
|
+
if (resolveToFunction(argRef)) return false;
|
|
38141
38564
|
const argIdentifier = argRef.identifier;
|
|
38142
38565
|
if (isImportBindingRef(argRef) && !isCalleePosition(argIdentifier)) return false;
|
|
38143
38566
|
if (isNodeOfType(argIdentifier, "Identifier") && argIdentifier.name === "undefined") return false;
|