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("./utils/ast-utils"); 13 14//------------------------------------------------------------------------------ 15// Rule Definition 16//------------------------------------------------------------------------------ 17 18module.exports = { 19 meta: { 20 type: "suggestion", 21 22 docs: { 23 description: "disallow unnecessary labels", 24 category: "Best Practices", 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}; 150