]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Rule to disallow unnecessary labels | |
3 | * @author Toru Nagashima | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | //------------------------------------------------------------------------------ | |
9 | // Requirements | |
10 | //------------------------------------------------------------------------------ | |
11 | ||
12 | const astUtils = require("./utils/ast-utils"); | |
13 | ||
14 | //------------------------------------------------------------------------------ | |
15 | // Rule Definition | |
16 | //------------------------------------------------------------------------------ | |
17 | ||
34eeec05 | 18 | /** @type {import('../shared/types').Rule} */ |
eb39fafa DC |
19 | module.exports = { |
20 | meta: { | |
21 | type: "suggestion", | |
22 | ||
23 | docs: { | |
8f9d1d4d | 24 | description: "Disallow unnecessary labels", |
eb39fafa DC |
25 | recommended: false, |
26 | url: "https://eslint.org/docs/rules/no-extra-label" | |
27 | }, | |
28 | ||
29 | schema: [], | |
30 | fixable: "code", | |
31 | ||
32 | messages: { | |
33 | unexpected: "This label '{{name}}' is unnecessary." | |
34 | } | |
35 | }, | |
36 | ||
37 | create(context) { | |
38 | const sourceCode = context.getSourceCode(); | |
39 | let scopeInfo = null; | |
40 | ||
41 | /** | |
42 | * Creates a new scope with a breakable statement. | |
43 | * @param {ASTNode} node A node to create. This is a BreakableStatement. | |
44 | * @returns {void} | |
45 | */ | |
46 | function enterBreakableStatement(node) { | |
47 | scopeInfo = { | |
48 | label: node.parent.type === "LabeledStatement" ? node.parent.label : null, | |
49 | breakable: true, | |
50 | upper: scopeInfo | |
51 | }; | |
52 | } | |
53 | ||
54 | /** | |
55 | * Removes the top scope of the stack. | |
56 | * @returns {void} | |
57 | */ | |
58 | function exitBreakableStatement() { | |
59 | scopeInfo = scopeInfo.upper; | |
60 | } | |
61 | ||
62 | /** | |
63 | * Creates a new scope with a labeled statement. | |
64 | * | |
65 | * This ignores it if the body is a breakable statement. | |
66 | * In this case it's handled in the `enterBreakableStatement` function. | |
67 | * @param {ASTNode} node A node to create. This is a LabeledStatement. | |
68 | * @returns {void} | |
69 | */ | |
70 | function enterLabeledStatement(node) { | |
71 | if (!astUtils.isBreakableStatement(node.body)) { | |
72 | scopeInfo = { | |
73 | label: node.label, | |
74 | breakable: false, | |
75 | upper: scopeInfo | |
76 | }; | |
77 | } | |
78 | } | |
79 | ||
80 | /** | |
81 | * Removes the top scope of the stack. | |
82 | * | |
83 | * This ignores it if the body is a breakable statement. | |
84 | * In this case it's handled in the `exitBreakableStatement` function. | |
85 | * @param {ASTNode} node A node. This is a LabeledStatement. | |
86 | * @returns {void} | |
87 | */ | |
88 | function exitLabeledStatement(node) { | |
89 | if (!astUtils.isBreakableStatement(node.body)) { | |
90 | scopeInfo = scopeInfo.upper; | |
91 | } | |
92 | } | |
93 | ||
94 | /** | |
95 | * Reports a given control node if it's unnecessary. | |
96 | * @param {ASTNode} node A node. This is a BreakStatement or a | |
97 | * ContinueStatement. | |
98 | * @returns {void} | |
99 | */ | |
100 | function reportIfUnnecessary(node) { | |
101 | if (!node.label) { | |
102 | return; | |
103 | } | |
104 | ||
105 | const labelNode = node.label; | |
106 | ||
107 | for (let info = scopeInfo; info !== null; info = info.upper) { | |
108 | if (info.breakable || info.label && info.label.name === labelNode.name) { | |
109 | if (info.breakable && info.label && info.label.name === labelNode.name) { | |
110 | context.report({ | |
111 | node: labelNode, | |
112 | messageId: "unexpected", | |
113 | data: labelNode, | |
114 | fix(fixer) { | |
115 | const breakOrContinueToken = sourceCode.getFirstToken(node); | |
116 | ||
117 | if (sourceCode.commentsExistBetween(breakOrContinueToken, labelNode)) { | |
118 | return null; | |
119 | } | |
120 | ||
121 | return fixer.removeRange([breakOrContinueToken.range[1], labelNode.range[1]]); | |
122 | } | |
123 | }); | |
124 | } | |
125 | return; | |
126 | } | |
127 | } | |
128 | } | |
129 | ||
130 | return { | |
131 | WhileStatement: enterBreakableStatement, | |
132 | "WhileStatement:exit": exitBreakableStatement, | |
133 | DoWhileStatement: enterBreakableStatement, | |
134 | "DoWhileStatement:exit": exitBreakableStatement, | |
135 | ForStatement: enterBreakableStatement, | |
136 | "ForStatement:exit": exitBreakableStatement, | |
137 | ForInStatement: enterBreakableStatement, | |
138 | "ForInStatement:exit": exitBreakableStatement, | |
139 | ForOfStatement: enterBreakableStatement, | |
140 | "ForOfStatement:exit": exitBreakableStatement, | |
141 | SwitchStatement: enterBreakableStatement, | |
142 | "SwitchStatement:exit": exitBreakableStatement, | |
143 | LabeledStatement: enterLabeledStatement, | |
144 | "LabeledStatement:exit": exitLabeledStatement, | |
145 | BreakStatement: reportIfUnnecessary, | |
146 | ContinueStatement: reportIfUnnecessary | |
147 | }; | |
148 | } | |
149 | }; |