no-collection-size-mischeck.js 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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/S3981
  22. const parser_services_1 = require("../utils/parser-services");
  23. const docs_url_1 = require("../utils/docs-url");
  24. const CollectionLike = ['Array', 'Map', 'Set', 'WeakMap', 'WeakSet'];
  25. const CollectionSizeLike = ['length', 'size'];
  26. const rule = {
  27. meta: {
  28. messages: {
  29. fixCollectionSizeCheck: 'Fix this expression; {{propertyName}} of "{{objectName}}" is always greater or equal to zero.',
  30. suggestFixedSizeCheck: 'Use "{{operator}}" for {{operation}} check',
  31. },
  32. schema: [],
  33. type: 'problem',
  34. hasSuggestions: true,
  35. docs: {
  36. description: 'Collection sizes and array length comparisons should make sense',
  37. recommended: 'error',
  38. url: (0, docs_url_1.default)(__filename),
  39. },
  40. },
  41. create(context) {
  42. const services = context.parserServices;
  43. const isTypeCheckerAvailable = (0, parser_services_1.isRequiredParserServices)(services);
  44. return {
  45. BinaryExpression: (node) => {
  46. const expr = node;
  47. if (['<', '>='].includes(expr.operator)) {
  48. const lhs = expr.left;
  49. const rhs = expr.right;
  50. if (isZeroLiteral(rhs) && lhs.type === 'MemberExpression') {
  51. const { object, property } = lhs;
  52. if (property.type === 'Identifier' &&
  53. CollectionSizeLike.includes(property.name) &&
  54. (!isTypeCheckerAvailable || isCollection(object, services))) {
  55. context.report({
  56. messageId: 'fixCollectionSizeCheck',
  57. data: {
  58. propertyName: property.name,
  59. objectName: context.getSourceCode().getText(object),
  60. },
  61. node,
  62. suggest: getSuggestion(expr, property.name, context),
  63. });
  64. }
  65. }
  66. }
  67. },
  68. };
  69. },
  70. };
  71. function isZeroLiteral(node) {
  72. return node.type === 'Literal' && node.value === 0;
  73. }
  74. function isCollection(node, services) {
  75. const checker = services.program.getTypeChecker();
  76. const tp = checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(node));
  77. return !!tp.symbol && CollectionLike.includes(tp.symbol.name);
  78. }
  79. function getSuggestion(expr, operation, context) {
  80. const { left, operator } = expr;
  81. const operatorToken = context
  82. .getSourceCode()
  83. .getTokenAfter(left, token => token.value === operator);
  84. const fixedOperator = operator === '<' ? '==' : '>';
  85. return [
  86. {
  87. messageId: 'suggestFixedSizeCheck',
  88. data: {
  89. operation,
  90. operator: fixedOperator,
  91. },
  92. fix: fixer => fixer.replaceText(operatorToken, fixedOperator),
  93. },
  94. ];
  95. }
  96. module.exports = rule;
  97. //# sourceMappingURL=no-collection-size-mischeck.js.map