Welcome to mirror list, hosted at ThFree Co, Russian Federation.

no-this-before-super.js « rules « lib « eslint « tools - github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: bd0836eec78cd2707592af63963bbf3ccb279b77 (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
/**
 * @fileoverview A rule to disallow using `this`/`super` before `super()`.
 * @author Toru Nagashima
 * @copyright 2015 Toru Nagashima. All rights reserved.
 */

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

    /**
     * Searches a class node that a node is belonging to.
     * @param {Node} node - A node to start searching.
     * @returns {ClassDeclaration|ClassExpression|null} the found class node, or `null`.
     */
    function getClassInAncestor(node) {
        while (node != null) {
            if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
                return node;
            }
            node = node.parent;
        }
        /* istanbul ignore next */
        return null;
    }

    /**
     * Checks whether or not a node is the null literal.
     * @param {Node} node - A node to check.
     * @returns {boolean} whether or not a node is the null literal.
     */
    function isNullLiteral(node) {
        return node != null && node.type === "Literal" && node.value === null;
    }

    /**
     * Checks whether or not a node is the callee of a call expression.
     * @param {Node} node - A node to check.
     * @returns {boolean} whether or not a node is the callee of a call expression.
     */
    function isCallee(node) {
        return node != null && node.parent.type === "CallExpression" && node.parent.callee === node;
    }

    /**
     * Checks whether or not the current traversal context is before `super()`.
     * @param {object} item - A checking context.
     * @returns {boolean} whether or not the current traversal context is before `super()`.
     */
    function isBeforeSuperCalling(item) {
        return (
            item != null &&
            item.scope === context.getScope().variableScope.upper.variableScope &&
            item.superCalled === false
        );
    }

    var stack = [];

    return {
        /**
         * Start checking.
         * @param {MethodDefinition} node - A target node.
         * @returns {void}
         */
        "MethodDefinition": function(node) {
            if (node.kind !== "constructor") {
                return;
            }
            stack.push({
                thisOrSuperBeforeSuperCalled: [],
                superCalled: false,
                scope: context.getScope().variableScope
            });
        },

        /**
         * Treats the result of checking and reports invalid `this`/`super`.
         * @param {MethodDefinition} node - A target node.
         * @returns {void}
         */
        "MethodDefinition:exit": function(node) {
            if (node.kind !== "constructor") {
                return;
            }
            var result = stack.pop();

            // Skip if it has no extends or `extends null`.
            var classNode = getClassInAncestor(node);
            if (classNode == null || classNode.superClass == null || isNullLiteral(classNode.superClass)) {
                return;
            }

            // Reports.
            result.thisOrSuperBeforeSuperCalled.forEach(function(thisOrSuper) {
                var type = (thisOrSuper.type === "Super" ? "super" : "this");
                context.report(thisOrSuper, "\"{{type}}\" is not allowed before super()", {type: type});
            });
        },

        /**
         * Marks the node if is before `super()`.
         * @param {ThisExpression} node - A target node.
         * @returns {void}
         */
        "ThisExpression": function(node) {
            var item = stack[stack.length - 1];
            if (isBeforeSuperCalling(item)) {
                item.thisOrSuperBeforeSuperCalled.push(node);
            }
        },

        /**
         * Marks the node if is before `super()`. (exclude `super()` itself)
         * @param {Super} node - A target node.
         * @returns {void}
         */
        "Super": function(node) {
            var item = stack[stack.length - 1];
            if (isBeforeSuperCalling(item) && isCallee(node) === false) {
                item.thisOrSuperBeforeSuperCalled.push(node);
            }
        },

        /**
         * Marks `super()` called.
         * To catch `super(this.a);`, marks on `CallExpression:exit`.
         * @param {CallExpression} node - A target node.
         * @returns {void}
         */
        "CallExpression:exit": function(node) {
            var item = stack[stack.length - 1];
            if (isBeforeSuperCalling(item) && node.callee.type === "Super") {
                item.superCalled = true;
            }
        }
    };
};

module.exports.schema = [];