summaryrefslogtreecommitdiff
path: root/tools/eslint/lib/rules/init-declarations.js
blob: 10fbf185c160c41f383302456c1ecd5be6865ceb (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
/**
 * @fileoverview A rule to control the style of variable initializations.
 * @author Colin Ihrig
 * @copyright 2015 Colin Ihrig. All rights reserved.
 */

"use strict";

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks whether or not a given node is a for loop.
 * @param {ASTNode} block - A node to check.
 * @returns {boolean} `true` when the node is a for loop.
 */
function isForLoop(block) {
    return block.type === "ForInStatement" ||
    block.type === "ForOfStatement" ||
    block.type === "ForStatement";
}

/**
 * Checks whether or not a given declarator node has its initializer.
 * @param {ASTNode} node - A declarator node to check.
 * @returns {boolean} `true` when the node has its initializer.
 */
function isInitialized(node) {
    var declaration = node.parent;
    var block = declaration.parent;

    if (isForLoop(block)) {
        if (block.type === "ForStatement") {
            return block.init === declaration;
        }
        return block.left === declaration;
    }
    return Boolean(node.init);
}

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

module.exports = function(context) {

    var MODE_ALWAYS = "always",
        MODE_NEVER = "never";

    var mode = context.options[0] || MODE_ALWAYS;
    var params = context.options[1] || {};

    //--------------------------------------------------------------------------
    // Public API
    //--------------------------------------------------------------------------

    return {
        "VariableDeclaration:exit": function(node) {

            var kind = node.kind,
                declarations = node.declarations;

            for (var i = 0; i < declarations.length; ++i) {
                var declaration = declarations[i],
                    id = declaration.id,
                    initialized = isInitialized(declaration),
                    isIgnoredForLoop = params.ignoreForLoopInit && isForLoop(node.parent);

                if (id.type !== "Identifier") {
                    continue;
                }

                if (mode === MODE_ALWAYS && !initialized) {
                    context.report(declaration, "Variable '" + id.name + "' should be initialized on declaration.");
                } else if (mode === MODE_NEVER && kind !== "const" && initialized && !isIgnoredForLoop) {
                    context.report(declaration, "Variable '" + id.name + "' should not be initialized on declaration.");
                }
            }
        }
    };
};

module.exports.schema = {
    "anyOf": [
        {
            "type": "array",
            "items": [
                {
                    "enum": ["always"]
                }
            ],
            "minItems": 0,
            "maxItems": 1
        },
        {
            "type": "array",
            "items": [
                {
                    "enum": ["never"]
                },
                {
                    "type": "object",
                    "properties": {
                        "ignoreForLoopInit": {
                            "type": "boolean"
                        }
                    },
                    "additionalProperties": false
                }
            ],
            "minItems": 0,
            "maxItems": 2
        }
    ]
};