| /** |
| * @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 => fixer.removeRange([sourceCode.getFirstToken(node).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 |
| }; |
| } |
| }; |