no-one-iteration-loop.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. "use strict";
  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. messages: {
  27. refactorLoop: 'Refactor this loop to do more than one iteration.',
  28. },
  29. schema: [],
  30. type: 'problem',
  31. docs: {
  32. description: 'Loops with at most one iteration should be refactored',
  33. recommended: 'error',
  34. url: (0, docs_url_1.default)(__filename),
  35. },
  36. },
  37. // @ts-ignore The typings of @typescript-eslint/experimental-utils does not contain the 'onX' methods.
  38. create(context) {
  39. const loopingNodes = new Set();
  40. const loops = new Set();
  41. const loopsAndTheirSegments = [];
  42. const currentCodePaths = [];
  43. return {
  44. ForStatement(node) {
  45. loops.add(node);
  46. },
  47. WhileStatement(node) {
  48. loops.add(node);
  49. },
  50. DoWhileStatement(node) {
  51. loops.add(node);
  52. },
  53. onCodePathStart(codePath) {
  54. currentCodePaths.push(codePath);
  55. },
  56. onCodePathEnd() {
  57. currentCodePaths.pop();
  58. },
  59. 'WhileStatement > *'(node) {
  60. visitLoopChild(node.parent);
  61. },
  62. 'ForStatement > *'(node) {
  63. visitLoopChild(node.parent);
  64. },
  65. onCodePathSegmentLoop(_, toSegment, node) {
  66. if ((0, nodes_1.isContinueStatement)(node)) {
  67. loopsAndTheirSegments.forEach(({ segments, loop }) => {
  68. if (segments.includes(toSegment)) {
  69. loopingNodes.add(loop);
  70. }
  71. });
  72. }
  73. else {
  74. loopingNodes.add(node);
  75. }
  76. },
  77. 'Program:exit'() {
  78. loops.forEach(loop => {
  79. if (!loopingNodes.has(loop)) {
  80. context.report({
  81. messageId: 'refactorLoop',
  82. loc: context.getSourceCode().getFirstToken(loop).loc,
  83. });
  84. }
  85. });
  86. },
  87. };
  88. // Required to correctly process "continue" looping.
  89. // When a loop has a "continue" statement, this "continue" statement triggers a "onCodePathSegmentLoop" event,
  90. // and the corresponding event node is that "continue" statement. Current implementation is based on the fact
  91. // that the "onCodePathSegmentLoop" event is triggered with a loop node. To work this special case around,
  92. // we visit loop children and collect corresponding path segments as these segments are "toSegment"
  93. // in "onCodePathSegmentLoop" event.
  94. function visitLoopChild(parent) {
  95. if (currentCodePaths.length > 0) {
  96. const currentCodePath = currentCodePaths[currentCodePaths.length - 1];
  97. loopsAndTheirSegments.push({ segments: currentCodePath.currentSegments, loop: parent });
  98. }
  99. }
  100. },
  101. };
  102. module.exports = rule;
  103. //# sourceMappingURL=no-one-iteration-loop.js.map