An error occurred while loading the file. Please try again.
no-extra-label.js 5.03 KiB
/**
 * @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 }; } };