summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/rules/no-useless-constructor.js
blob: 7cf033805f99d9c84a8eadc36e48661773bc14b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/**
 * @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 construtor 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
//------------------------------------------------------------------------------

module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "disallow unnecessary constructors",
            category: "ECMAScript 6",
            recommended: false,
            url: "https://eslint.org/docs/rules/no-useless-constructor"
        },

        schema: []
    },

    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;
            }

            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,
                    message: "Useless constructor."
                });
            }
        }

        return {
            MethodDefinition: checkForConstructor
        };
    }
};