Permalink
Cannot retrieve contributors at this time
| /** | |
| * @fileoverview Rule to disallow unnecessary labels | |
| * @author Toru Nagashima | |
| */ | |
| "use strict"; | |
| //------------------------------------------------------------------------------ | |
| // Requirements | |
| //------------------------------------------------------------------------------ | |
| const astUtils = require("./utils/ast-utils"); | |
| //------------------------------------------------------------------------------ | |
| // Rule Definition | |
| //------------------------------------------------------------------------------ | |
| module.exports = { | |
| meta: { | |
| type: "suggestion", | |
| docs: { | |
| description: "disallow unnecessary labels", | |
| category: "Best Practices", | |
| recommended: false, | |
| url: "https://eslint.org/docs/rules/no-extra-label" | |
| }, | |
| schema: [], | |
| fixable: "code", | |
| messages: { | |
| unexpected: "This label '{{name}}' is unnecessary." | |
| } | |
| }, | |
| create(context) { | |
| const sourceCode = context.getSourceCode(); | |
| let scopeInfo = null; | |
| /** | |
| * Creates a new scope with a breakable statement. | |
| * @param {ASTNode} node A node to create. This is a BreakableStatement. | |
| * @returns {void} | |
| */ | |
| function enterBreakableStatement(node) { | |
| scopeInfo = { | |
| label: node.parent.type === "LabeledStatement" ? node.parent.label : null, | |
| breakable: true, | |
| upper: scopeInfo | |
| }; | |
| } | |
| /** | |
| * Removes the top scope of the stack. | |
| * @returns {void} | |
| */ | |
| function exitBreakableStatement() { | |
| scopeInfo = scopeInfo.upper; | |
| } | |
| /** | |
| * Creates a new scope with a labeled statement. | |
| * | |
| * This ignores it if the body is a breakable statement. | |
| * In this case it's handled in the `enterBreakableStatement` function. | |
| * @param {ASTNode} node A node to create. This is a LabeledStatement. | |
| * @returns {void} | |
| */ | |
| function enterLabeledStatement(node) { | |
| if (!astUtils.isBreakableStatement(node.body)) { | |
| scopeInfo = { | |
| label: node.label, | |
| breakable: false, | |
| upper: scopeInfo | |
| }; | |
| } | |
| } | |
| /** | |
| * Removes the top scope of the stack. | |
| * | |
| * This ignores it if the body is a breakable statement. | |
| * In this case it's handled in the `exitBreakableStatement` function. | |
| * @param {ASTNode} node A node. This is a LabeledStatement. | |
| * @returns {void} | |
| */ | |
| function exitLabeledStatement(node) { | |
| if (!astUtils.isBreakableStatement(node.body)) { | |
| scopeInfo = scopeInfo.upper; | |
| } | |
| } | |
| /** | |
| * Reports a given control node if it's unnecessary. | |
| * @param {ASTNode} node A node. This is a BreakStatement or a | |
| * ContinueStatement. | |
| * @returns {void} | |
| */ | |
| function reportIfUnnecessary(node) { | |
| if (!node.label) { | |
| return; | |
| } | |
| const labelNode = node.label; | |
| for (let info = scopeInfo; info !== null; info = info.upper) { | |
| if (info.breakable || info.label && info.label.name === labelNode.name) { | |
| if (info.breakable && info.label && info.label.name === labelNode.name) { | |
| context.report({ | |
| node: labelNode, | |
| messageId: "unexpected", | |
| data: labelNode, | |
| fix(fixer) { | |
| const breakOrContinueToken = sourceCode.getFirstToken(node); | |
| if (sourceCode.commentsExistBetween(breakOrContinueToken, labelNode)) { | |
| return null; | |
| } | |
| return fixer.removeRange([breakOrContinueToken.range[1], labelNode.range[1]]); | |
| } | |
| }); | |
| } | |
| return; | |
| } | |
| } | |
| } | |
| return { | |
| WhileStatement: enterBreakableStatement, | |
| "WhileStatement:exit": exitBreakableStatement, | |
| DoWhileStatement: enterBreakableStatement, | |
| "DoWhileStatement:exit": exitBreakableStatement, | |
| ForStatement: enterBreakableStatement, | |
| "ForStatement:exit": exitBreakableStatement, | |
| ForInStatement: enterBreakableStatement, | |
| "ForInStatement:exit": exitBreakableStatement, | |
| ForOfStatement: enterBreakableStatement, | |
| "ForOfStatement:exit": exitBreakableStatement, | |
| SwitchStatement: enterBreakableStatement, | |
| "SwitchStatement:exit": exitBreakableStatement, | |
| LabeledStatement: enterLabeledStatement, | |
| "LabeledStatement:exit": exitLabeledStatement, | |
| BreakStatement: reportIfUnnecessary, | |
| ContinueStatement: reportIfUnnecessary | |
| }; | |
| } | |
| }; |