190 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * @fileoverview Rule to flag the use of redundant constructors in classes.
 | 
						|
 * @author Alberto Rodríguez
 | 
						|
 */
 | 
						|
"use strict";
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
// Helpers
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether a given array of statements is a single call of `super`.
 | 
						|
 * @param {ASTNode[]} body An array of statements to check.
 | 
						|
 * @returns {boolean} `true` if the body is a single call of `super`.
 | 
						|
 */
 | 
						|
function isSingleSuperCall(body) {
 | 
						|
    return (
 | 
						|
        body.length === 1 &&
 | 
						|
        body[0].type === "ExpressionStatement" &&
 | 
						|
        body[0].expression.type === "CallExpression" &&
 | 
						|
        body[0].expression.callee.type === "Super"
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether a given node is a pattern which doesn't have any side effects.
 | 
						|
 * Default parameters and Destructuring parameters can have side effects.
 | 
						|
 * @param {ASTNode} node A pattern node.
 | 
						|
 * @returns {boolean} `true` if the node doesn't have any side effects.
 | 
						|
 */
 | 
						|
function isSimple(node) {
 | 
						|
    return node.type === "Identifier" || node.type === "RestElement";
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether a given array of expressions is `...arguments` or not.
 | 
						|
 * `super(...arguments)` passes all arguments through.
 | 
						|
 * @param {ASTNode[]} superArgs An array of expressions to check.
 | 
						|
 * @returns {boolean} `true` if the superArgs is `...arguments`.
 | 
						|
 */
 | 
						|
function isSpreadArguments(superArgs) {
 | 
						|
    return (
 | 
						|
        superArgs.length === 1 &&
 | 
						|
        superArgs[0].type === "SpreadElement" &&
 | 
						|
        superArgs[0].argument.type === "Identifier" &&
 | 
						|
        superArgs[0].argument.name === "arguments"
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether given 2 nodes are identifiers which have the same name or not.
 | 
						|
 * @param {ASTNode} ctorParam A node to check.
 | 
						|
 * @param {ASTNode} superArg A node to check.
 | 
						|
 * @returns {boolean} `true` if the nodes are identifiers which have the same
 | 
						|
 *      name.
 | 
						|
 */
 | 
						|
function isValidIdentifierPair(ctorParam, superArg) {
 | 
						|
    return (
 | 
						|
        ctorParam.type === "Identifier" &&
 | 
						|
        superArg.type === "Identifier" &&
 | 
						|
        ctorParam.name === superArg.name
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether given 2 nodes are a rest/spread pair which has the same values.
 | 
						|
 * @param {ASTNode} ctorParam A node to check.
 | 
						|
 * @param {ASTNode} superArg A node to check.
 | 
						|
 * @returns {boolean} `true` if the nodes are a rest/spread pair which has the
 | 
						|
 *      same values.
 | 
						|
 */
 | 
						|
function isValidRestSpreadPair(ctorParam, superArg) {
 | 
						|
    return (
 | 
						|
        ctorParam.type === "RestElement" &&
 | 
						|
        superArg.type === "SpreadElement" &&
 | 
						|
        isValidIdentifierPair(ctorParam.argument, superArg.argument)
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether given 2 nodes have the same value or not.
 | 
						|
 * @param {ASTNode} ctorParam A node to check.
 | 
						|
 * @param {ASTNode} superArg A node to check.
 | 
						|
 * @returns {boolean} `true` if the nodes have the same value or not.
 | 
						|
 */
 | 
						|
function isValidPair(ctorParam, superArg) {
 | 
						|
    return (
 | 
						|
        isValidIdentifierPair(ctorParam, superArg) ||
 | 
						|
        isValidRestSpreadPair(ctorParam, superArg)
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether the parameters of a constructor and the arguments of `super()`
 | 
						|
 * have the same values or not.
 | 
						|
 * @param {ASTNode} ctorParams The parameters of a constructor to check.
 | 
						|
 * @param {ASTNode} superArgs The arguments of `super()` to check.
 | 
						|
 * @returns {boolean} `true` if those have the same values.
 | 
						|
 */
 | 
						|
function isPassingThrough(ctorParams, superArgs) {
 | 
						|
    if (ctorParams.length !== superArgs.length) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    for (let i = 0; i < ctorParams.length; ++i) {
 | 
						|
        if (!isValidPair(ctorParams[i], superArgs[i])) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether the constructor body is a redundant super call.
 | 
						|
 * @param {Array} body constructor body content.
 | 
						|
 * @param {Array} ctorParams The params to check against super call.
 | 
						|
 * @returns {boolean} true if the constructor body is redundant
 | 
						|
 */
 | 
						|
function isRedundantSuperCall(body, ctorParams) {
 | 
						|
    return (
 | 
						|
        isSingleSuperCall(body) &&
 | 
						|
        ctorParams.every(isSimple) &&
 | 
						|
        (
 | 
						|
            isSpreadArguments(body[0].expression.arguments) ||
 | 
						|
            isPassingThrough(ctorParams, body[0].expression.arguments)
 | 
						|
        )
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
// Rule Definition
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
/** @type {import('../shared/types').Rule} */
 | 
						|
module.exports = {
 | 
						|
    meta: {
 | 
						|
        type: "suggestion",
 | 
						|
 | 
						|
        docs: {
 | 
						|
            description: "Disallow unnecessary constructors",
 | 
						|
            recommended: false,
 | 
						|
            url: "https://eslint.org/docs/latest/rules/no-useless-constructor"
 | 
						|
        },
 | 
						|
 | 
						|
        schema: [],
 | 
						|
 | 
						|
        messages: {
 | 
						|
            noUselessConstructor: "Useless constructor."
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    create(context) {
 | 
						|
 | 
						|
        /**
 | 
						|
         * Checks whether a node is a redundant constructor
 | 
						|
         * @param {ASTNode} node node to check
 | 
						|
         * @returns {void}
 | 
						|
         */
 | 
						|
        function checkForConstructor(node) {
 | 
						|
            if (node.kind !== "constructor") {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            /*
 | 
						|
             * Prevent crashing on parsers which do not require class constructor
 | 
						|
             * to have a body, e.g. typescript and flow
 | 
						|
             */
 | 
						|
            if (!node.value.body) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            const body = node.value.body.body;
 | 
						|
            const ctorParams = node.value.params;
 | 
						|
            const superClass = node.parent.parent.superClass;
 | 
						|
 | 
						|
            if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) {
 | 
						|
                context.report({
 | 
						|
                    node,
 | 
						|
                    messageId: "noUselessConstructor"
 | 
						|
                });
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return {
 | 
						|
            MethodDefinition: checkForConstructor
 | 
						|
        };
 | 
						|
    }
 | 
						|
};
 |