import { containsAllTypesByName, getConstrainedTypeAtLocation, isTypeArrayTypeOrUnionOfArrayTypes, } from "@typescript-eslint/type-utils";
import { ESLintUtils } from "@typescript-eslint/utils";
import { setHas } from "ts-extras";
import { ruleCreator } from "../_internal/rule-creator.js";
const readonlyArrayTypeNames = new Set(["ReadonlyArray"]);
const isArrayLikeType = (typeChecker, type) => {
    const apparentType = typeChecker.getApparentType(type);
    return (typeChecker.isArrayType(apparentType) ||
        typeChecker.isTupleType(apparentType) ||
        isTypeArrayTypeOrUnionOfArrayTypes(type, typeChecker) ||
        containsAllTypesByName(type, false, readonlyArrayTypeNames, true));
};
const creatorMethodNames = new Set([
    "concat",
    "entries",
    "filter",
    "keys",
    "map",
    "slice",
    "splice",
    "values",
]);
const isArrayFactoryCallee = (callee) => {
    if (callee.type === "Identifier") {
        return callee.name === "Array";
    }
    if (callee.type === "MemberExpression" &&
        callee.object.type === "Identifier" &&
        callee.object.name === "Array" &&
        callee.property.type === "Identifier") {
        return callee.property.name === "from" || callee.property.name === "of";
    }
    return false;
};
const isNewArray = (node) => {
    if (node.type === "ArrayExpression") {
        return true;
    }
    if (node.type === "CallExpression") {
        return isArrayFactoryCallee(node.callee);
    }
    return false;
};
const mutatesReferencedArray = (callExpression) => {
    if (callExpression.callee.type !== "MemberExpression") {
        return true;
    }
    const { object, property } = callExpression.callee;
    if (property.type === "Identifier" &&
        setHas(creatorMethodNames, property.name)) {
        return false;
    }
    if (isNewArray(object)) {
        return false;
    }
    if (object.type === "CallExpression") {
        return mutatesReferencedArray(object);
    }
    return true;
};
/**
 * Disallow assigning arrays returned by mutating methods like `fill`,
 * `reverse`, and `sort`.
 */
const rule = ruleCreator({
    create: (context) => {
        const parserServices = ESLintUtils.getParserServices(context);
        const typeChecker = parserServices.program.getTypeChecker();
        return {
            "CallExpression[callee.type='MemberExpression'][callee.property.type='Identifier'][callee.property.name=/^(?:fill|reverse|sort)$/]": (callExpression) => {
                const { callee } = callExpression;
                if (callee.type !== "MemberExpression") {
                    return;
                }
                const { property } = callee;
                if (property.type !== "Identifier") {
                    return;
                }
                if (callExpression.parent?.type ===
                    "ExpressionStatement") {
                    return;
                }
                const objectType = getConstrainedTypeAtLocation(parserServices, callee.object);
                if (!isArrayLikeType(typeChecker, objectType)) {
                    return;
                }
                if (!mutatesReferencedArray(callExpression)) {
                    return;
                }
                context.report({
                    messageId: "forbidden",
                    node: property,
                });
            },
        };
    },
    defaultOptions: [],
    meta: {
        deprecated: false,
        docs: {
            deprecated: false,
            description: "disallow assigning values returned from mutating array methods.",
            frozen: false,
            recommended: true,
            requiresTypeChecking: true,
            url: "https://nick2bad4u.github.io/eslint-plugin-etc-misc/docs/rules/no-assign-mutated-array",
        },
        hasSuggestions: false,
        messages: {
            forbidden: "Assignment of mutated arrays is forbidden.",
        },
        schema: [],
        type: "problem",
    },
    name: "no-assign-mutated-array",
});
export default rule;
//# sourceMappingURL=no-assign-mutated-array.js.map