no-identical-expressions.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  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/S1764
  22. const nodes_1 = require("../utils/nodes");
  23. const equivalence_1 = require("../utils/equivalence");
  24. const locations_1 = require("../utils/locations");
  25. const docs_url_1 = require("../utils/docs-url");
  26. const EQUALITY_OPERATOR_TOKEN_KINDS = new Set(['==', '===', '!=', '!==']);
  27. // consider only binary expressions with these operators
  28. const RELEVANT_OPERATOR_TOKEN_KINDS = new Set([
  29. '&&',
  30. '||',
  31. '/',
  32. '-',
  33. '<<',
  34. '>>',
  35. '<',
  36. '<=',
  37. '>',
  38. '>=',
  39. ]);
  40. function hasRelevantOperator(node) {
  41. return (RELEVANT_OPERATOR_TOKEN_KINDS.has(node.operator) ||
  42. (EQUALITY_OPERATOR_TOKEN_KINDS.has(node.operator) && !hasIdentifierOperands(node)));
  43. }
  44. function hasIdentifierOperands(node) {
  45. return (0, nodes_1.isIdentifier)(node.left) && (0, nodes_1.isIdentifier)(node.right);
  46. }
  47. function isOneOntoOneShifting(node) {
  48. return node.operator === '<<' && (0, nodes_1.isLiteral)(node.left) && node.left.value === 1;
  49. }
  50. const message = 'Correct one of the identical sub-expressions on both sides of operator "{{operator}}"';
  51. const rule = {
  52. meta: {
  53. messages: {
  54. correctIdenticalSubExpressions: message,
  55. sonarRuntime: '{{sonarRuntimeData}}',
  56. },
  57. type: 'problem',
  58. docs: {
  59. description: 'Identical expressions should not be used on both sides of a binary operator',
  60. recommended: 'error',
  61. url: (0, docs_url_1.default)(__filename),
  62. },
  63. schema: [
  64. {
  65. // internal parameter
  66. enum: ['sonar-runtime'],
  67. },
  68. ],
  69. },
  70. create(context) {
  71. return {
  72. LogicalExpression(node) {
  73. check(node);
  74. },
  75. BinaryExpression(node) {
  76. check(node);
  77. },
  78. };
  79. function check(expr) {
  80. if (hasRelevantOperator(expr) &&
  81. !isOneOntoOneShifting(expr) &&
  82. (0, equivalence_1.areEquivalent)(expr.left, expr.right, context.getSourceCode())) {
  83. const secondaryLocations = [];
  84. if (expr.left.loc) {
  85. secondaryLocations.push((0, locations_1.issueLocation)(expr.left.loc));
  86. }
  87. (0, locations_1.report)(context, {
  88. messageId: 'correctIdenticalSubExpressions',
  89. data: {
  90. operator: expr.operator,
  91. },
  92. node: isSonarRuntime() ? expr.right : expr,
  93. }, secondaryLocations, message);
  94. }
  95. }
  96. function isSonarRuntime() {
  97. return context.options[context.options.length - 1] === 'sonar-runtime';
  98. }
  99. },
  100. };
  101. module.exports = rule;
  102. //# sourceMappingURL=no-identical-expressions.js.map