UNPKG

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