UNPKG

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