eslint-plugin-sonarjs
Version: 
SonarJS rules for ESLint
90 lines (89 loc) • 3.71 kB
JavaScript
;
/*
 * SonarQube JavaScript Plugin
 * Copyright (C) 2011-2025 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the Sonar Source-Available License for more details.
 *
 * You should have received a copy of the Sonar Source-Available License
 * along with this program; if not, see https://sonarsource.com/license/ssal/
 */
// https://sonarsource.github.io/rspec/#/rspec/S135/javascript
Object.defineProperty(exports, "__esModule", { value: true });
exports.rule = void 0;
const index_js_1 = require("../helpers/index.js");
const meta_js_1 = require("./meta.js");
exports.rule = {
    meta: (0, index_js_1.generateMeta)(meta_js_1.meta, undefined, true),
    create(context) {
        let jumpTargets = [];
        function enterScope() {
            jumpTargets.push(new JumpTarget());
        }
        function leaveScope() {
            jumpTargets.pop();
        }
        function increateNumberOfJumpsInScopes(jump, label) {
            for (const jumpTarget of [...jumpTargets].reverse()) {
                jumpTarget.jumps.push(jump);
                if (label === jumpTarget.label) {
                    break;
                }
            }
        }
        function leaveScopeAndCheckNumberOfJumps(node) {
            const jumps = jumpTargets.pop()?.jumps;
            if (jumps && jumps.length > 1) {
                const sourceCode = context.sourceCode;
                const firstToken = sourceCode.getFirstToken(node);
                (0, index_js_1.report)(context, {
                    loc: firstToken.loc,
                    message: 'Reduce the total number of "break" and "continue" statements in this loop to use one at most.',
                }, jumps.map(jmp => (0, index_js_1.toSecondaryLocation)(jmp, jmp.type === 'BreakStatement' ? '"break" statement.' : '"continue" statement.')));
            }
        }
        return {
            Program: () => {
                jumpTargets = [];
            },
            BreakStatement: (node) => {
                const breakStatement = node;
                increateNumberOfJumpsInScopes(breakStatement, breakStatement.label?.name);
            },
            ContinueStatement: (node) => {
                const continueStatement = node;
                increateNumberOfJumpsInScopes(continueStatement, continueStatement.label?.name);
            },
            SwitchStatement: enterScope,
            'SwitchStatement:exit': leaveScope,
            ForStatement: enterScope,
            'ForStatement:exit': leaveScopeAndCheckNumberOfJumps,
            ForInStatement: enterScope,
            'ForInStatement:exit': leaveScopeAndCheckNumberOfJumps,
            ForOfStatement: enterScope,
            'ForOfStatement:exit': leaveScopeAndCheckNumberOfJumps,
            WhileStatement: enterScope,
            'WhileStatement:exit': leaveScopeAndCheckNumberOfJumps,
            DoWhileStatement: enterScope,
            'DoWhileStatement:exit': leaveScopeAndCheckNumberOfJumps,
            LabeledStatement: (node) => {
                const labeledStatement = node;
                jumpTargets.push(new JumpTarget(labeledStatement.label.name));
            },
            'LabeledStatement:exit': leaveScope,
        };
    },
};
class JumpTarget {
    constructor(label) {
        this.jumps = [];
        this.label = label;
    }
}