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