UNPKG

4.16 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 },
24
25 schema: [
26 {
27 type: "object",
28 properties: {
29 allowLoop: {
30 type: "boolean"
31 },
32 allowSwitch: {
33 type: "boolean"
34 }
35 },
36 additionalProperties: false
37 }
38 ]
39 },
40
41 create(context) {
42 const options = context.options[0];
43 const allowLoop = Boolean(options && options.allowLoop);
44 const allowSwitch = Boolean(options && options.allowSwitch);
45 let scopeInfo = null;
46
47 /**
48 * Gets the kind of a given node.
49 *
50 * @param {ASTNode} node - A node to get.
51 * @returns {string} The kind of the node.
52 */
53 function getBodyKind(node) {
54 if (astUtils.isLoop(node)) {
55 return "loop";
56 }
57 if (node.type === "SwitchStatement") {
58 return "switch";
59 }
60 return "other";
61 }
62
63 /**
64 * Checks whether the label of a given kind is allowed or not.
65 *
66 * @param {string} kind - A kind to check.
67 * @returns {boolean} `true` if the kind is allowed.
68 */
69 function isAllowed(kind) {
70 switch (kind) {
71 case "loop": return allowLoop;
72 case "switch": return allowSwitch;
73 default: return false;
74 }
75 }
76
77 /**
78 * Checks whether a given name is a label of a loop or not.
79 *
80 * @param {string} label - A name of a label to check.
81 * @returns {boolean} `true` if the name is a label of a loop.
82 */
83 function getKind(label) {
84 let info = scopeInfo;
85
86 while (info) {
87 if (info.label === label) {
88 return info.kind;
89 }
90 info = info.upper;
91 }
92
93 /* istanbul ignore next: syntax error */
94 return "other";
95 }
96
97 //--------------------------------------------------------------------------
98 // Public
99 //--------------------------------------------------------------------------
100
101 return {
102 LabeledStatement(node) {
103 scopeInfo = {
104 label: node.label.name,
105 kind: getBodyKind(node.body),
106 upper: scopeInfo
107 };
108 },
109
110 "LabeledStatement:exit"(node) {
111 if (!isAllowed(scopeInfo.kind)) {
112 context.report({
113 node,
114 message: "Unexpected labeled statement."
115 });
116 }
117
118 scopeInfo = scopeInfo.upper;
119 },
120
121 BreakStatement(node) {
122 if (node.label && !isAllowed(getKind(node.label.name))) {
123 context.report({
124 node,
125 message: "Unexpected label in break statement."
126 });
127 }
128 },
129
130 ContinueStatement(node) {
131 if (node.label && !isAllowed(getKind(node.label.name))) {
132 context.report({
133 node,
134 message: "Unexpected label in continue statement."
135 });
136 }
137 }
138 };
139
140 }
141};