1/** 2 * @fileoverview Rule to disallow unused labels. 3 * @author Toru Nagashima 4 */ 5 6"use strict"; 7 8//------------------------------------------------------------------------------ 9// Rule Definition 10//------------------------------------------------------------------------------ 11 12module.exports = { 13 meta: { 14 type: "suggestion", 15 16 docs: { 17 description: "disallow unused labels", 18 category: "Best Practices", 19 recommended: true, 20 url: "https://eslint.org/docs/rules/no-unused-labels" 21 }, 22 23 schema: [], 24 25 fixable: "code", 26 27 messages: { 28 unused: "'{{name}}:' is defined but never used." 29 } 30 }, 31 32 create(context) { 33 const sourceCode = context.getSourceCode(); 34 let scopeInfo = null; 35 36 /** 37 * Adds a scope info to the stack. 38 * @param {ASTNode} node A node to add. This is a LabeledStatement. 39 * @returns {void} 40 */ 41 function enterLabeledScope(node) { 42 scopeInfo = { 43 label: node.label.name, 44 used: false, 45 upper: scopeInfo 46 }; 47 } 48 49 /** 50 * Removes the top of the stack. 51 * At the same time, this reports the label if it's never used. 52 * @param {ASTNode} node A node to report. This is a LabeledStatement. 53 * @returns {void} 54 */ 55 function exitLabeledScope(node) { 56 if (!scopeInfo.used) { 57 context.report({ 58 node: node.label, 59 messageId: "unused", 60 data: node.label, 61 fix(fixer) { 62 63 /* 64 * Only perform a fix if there are no comments between the label and the body. This will be the case 65 * when there is exactly one token/comment (the ":") between the label and the body. 66 */ 67 if (sourceCode.getTokenAfter(node.label, { includeComments: true }) === 68 sourceCode.getTokenBefore(node.body, { includeComments: true })) { 69 return fixer.removeRange([node.range[0], node.body.range[0]]); 70 } 71 72 return null; 73 } 74 }); 75 } 76 77 scopeInfo = scopeInfo.upper; 78 } 79 80 /** 81 * Marks the label of a given node as used. 82 * @param {ASTNode} node A node to mark. This is a BreakStatement or 83 * ContinueStatement. 84 * @returns {void} 85 */ 86 function markAsUsed(node) { 87 if (!node.label) { 88 return; 89 } 90 91 const label = node.label.name; 92 let info = scopeInfo; 93 94 while (info) { 95 if (info.label === label) { 96 info.used = true; 97 break; 98 } 99 info = info.upper; 100 } 101 } 102 103 return { 104 LabeledStatement: enterLabeledScope, 105 "LabeledStatement:exit": exitLabeledScope, 106 BreakStatement: markAsUsed, 107 ContinueStatement: markAsUsed 108 }; 109 } 110}; 111