• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Disallow Labeled Statements
3 * @author Nicholas C. Zakas
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const astUtils = require("./utils/ast-utils");
12
13//------------------------------------------------------------------------------
14// Rule Definition
15//------------------------------------------------------------------------------
16
17module.exports = {
18    meta: {
19        type: "suggestion",
20
21        docs: {
22            description: "disallow labeled statements",
23            category: "Best Practices",
24            recommended: false,
25            url: "https://eslint.org/docs/rules/no-labels"
26        },
27
28        schema: [
29            {
30                type: "object",
31                properties: {
32                    allowLoop: {
33                        type: "boolean",
34                        default: false
35                    },
36                    allowSwitch: {
37                        type: "boolean",
38                        default: false
39                    }
40                },
41                additionalProperties: false
42            }
43        ],
44
45        messages: {
46            unexpectedLabel: "Unexpected labeled statement.",
47            unexpectedLabelInBreak: "Unexpected label in break statement.",
48            unexpectedLabelInContinue: "Unexpected label in continue statement."
49        }
50    },
51
52    create(context) {
53        const options = context.options[0];
54        const allowLoop = options && options.allowLoop;
55        const allowSwitch = options && options.allowSwitch;
56        let scopeInfo = null;
57
58        /**
59         * Gets the kind of a given node.
60         * @param {ASTNode} node A node to get.
61         * @returns {string} The kind of the node.
62         */
63        function getBodyKind(node) {
64            if (astUtils.isLoop(node)) {
65                return "loop";
66            }
67            if (node.type === "SwitchStatement") {
68                return "switch";
69            }
70            return "other";
71        }
72
73        /**
74         * Checks whether the label of a given kind is allowed or not.
75         * @param {string} kind A kind to check.
76         * @returns {boolean} `true` if the kind is allowed.
77         */
78        function isAllowed(kind) {
79            switch (kind) {
80                case "loop": return allowLoop;
81                case "switch": return allowSwitch;
82                default: return false;
83            }
84        }
85
86        /**
87         * Checks whether a given name is a label of a loop or not.
88         * @param {string} label A name of a label to check.
89         * @returns {boolean} `true` if the name is a label of a loop.
90         */
91        function getKind(label) {
92            let info = scopeInfo;
93
94            while (info) {
95                if (info.label === label) {
96                    return info.kind;
97                }
98                info = info.upper;
99            }
100
101            /* istanbul ignore next: syntax error */
102            return "other";
103        }
104
105        //--------------------------------------------------------------------------
106        // Public
107        //--------------------------------------------------------------------------
108
109        return {
110            LabeledStatement(node) {
111                scopeInfo = {
112                    label: node.label.name,
113                    kind: getBodyKind(node.body),
114                    upper: scopeInfo
115                };
116            },
117
118            "LabeledStatement:exit"(node) {
119                if (!isAllowed(scopeInfo.kind)) {
120                    context.report({
121                        node,
122                        messageId: "unexpectedLabel"
123                    });
124                }
125
126                scopeInfo = scopeInfo.upper;
127            },
128
129            BreakStatement(node) {
130                if (node.label && !isAllowed(getKind(node.label.name))) {
131                    context.report({
132                        node,
133                        messageId: "unexpectedLabelInBreak"
134                    });
135                }
136            },
137
138            ContinueStatement(node) {
139                if (node.label && !isAllowed(getKind(node.label.name))) {
140                    context.report({
141                        node,
142                        messageId: "unexpectedLabelInContinue"
143                    });
144                }
145            }
146        };
147
148    }
149};
150