1 | ;
|
2 | /*
|
3 | * eslint-plugin-sonarjs
|
4 | * Copyright (C) 2018-2021 SonarSource SA
|
5 | * mailto:info AT sonarsource DOT com
|
6 | *
|
7 | * This program is free software; you can redistribute it and/or
|
8 | * modify it under the terms of the GNU Lesser General Public
|
9 | * License as published by the Free Software Foundation; either
|
10 | * version 3 of the License, or (at your option) any later version.
|
11 | *
|
12 | * This program is distributed in the hope that it will be useful,
|
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15 | * Lesser General Public License for more details.
|
16 | *
|
17 | * You should have received a copy of the GNU Lesser General Public License
|
18 | * along with this program; if not, write to the Free Software Foundation,
|
19 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
20 | */
|
21 | // https://sonarsource.github.io/rspec/#/rspec/S1751
|
22 | const nodes_1 = require("../utils/nodes");
|
23 | const docs_url_1 = require("../utils/docs-url");
|
24 | const rule = {
|
25 | meta: {
|
26 | type: 'problem',
|
27 | docs: {
|
28 | description: 'Loops with at most one iteration should be refactored',
|
29 | category: 'Possible Errors',
|
30 | recommended: 'error',
|
31 | url: docs_url_1.default(__filename),
|
32 | },
|
33 | },
|
34 | create(context) {
|
35 | const loopingNodes = new Set();
|
36 | const loops = new Set();
|
37 | const loopsAndTheirSegments = [];
|
38 | const currentCodePaths = [];
|
39 | return {
|
40 | ForStatement(node) {
|
41 | loops.add(node);
|
42 | },
|
43 | WhileStatement(node) {
|
44 | loops.add(node);
|
45 | },
|
46 | DoWhileStatement(node) {
|
47 | loops.add(node);
|
48 | },
|
49 | onCodePathStart(codePath) {
|
50 | currentCodePaths.push(codePath);
|
51 | },
|
52 | onCodePathEnd() {
|
53 | currentCodePaths.pop();
|
54 | },
|
55 | 'WhileStatement > *'(node) {
|
56 | visitLoopChild(node.parent);
|
57 | },
|
58 | 'ForStatement > *'(node) {
|
59 | visitLoopChild(node.parent);
|
60 | },
|
61 | onCodePathSegmentLoop(_, toSegment, node) {
|
62 | if (nodes_1.isContinueStatement(node)) {
|
63 | loopsAndTheirSegments.forEach(({ segments, loop }) => {
|
64 | if (segments.includes(toSegment)) {
|
65 | loopingNodes.add(loop);
|
66 | }
|
67 | });
|
68 | }
|
69 | else {
|
70 | loopingNodes.add(node);
|
71 | }
|
72 | },
|
73 | 'Program:exit'() {
|
74 | loops.forEach(loop => {
|
75 | if (!loopingNodes.has(loop)) {
|
76 | context.report({
|
77 | message: 'Refactor this loop to do more than one iteration.',
|
78 | loc: context.getSourceCode().getFirstToken(loop).loc,
|
79 | });
|
80 | }
|
81 | });
|
82 | },
|
83 | };
|
84 | // Required to correctly process "continue" looping.
|
85 | // When a loop has a "continue" statement, this "continue" statement triggers a "onCodePathSegmentLoop" event,
|
86 | // and the corresponding event node is that "continue" statement. Current implementation is based on the fact
|
87 | // that the "onCodePathSegmentLoop" event is triggerent with a loop node. To work this special case around,
|
88 | // we visit loop children and collect corresponding path segments as these segments are "toSegment"
|
89 | // in "onCodePathSegmentLoop" event.
|
90 | function visitLoopChild(parent) {
|
91 | if (currentCodePaths.length > 0) {
|
92 | const currentCodePath = currentCodePaths[currentCodePaths.length - 1];
|
93 | loopsAndTheirSegments.push({ segments: currentCodePath.currentSegments, loop: parent });
|
94 | }
|
95 | }
|
96 | },
|
97 | };
|
98 | module.exports = rule;
|
99 | //# sourceMappingURL=no-one-iteration-loop.js.map |
\ | No newline at end of file |