You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
4.8 KiB

4 years ago
  1. /**
  2. * @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals
  3. * @author Annie Zhang, Henry Zhu
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("./utils/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Helpers
  12. //------------------------------------------------------------------------------
  13. const radixMap = new Map([
  14. [2, { system: "binary", literalPrefix: "0b" }],
  15. [8, { system: "octal", literalPrefix: "0o" }],
  16. [16, { system: "hexadecimal", literalPrefix: "0x" }]
  17. ]);
  18. /**
  19. * Checks to see if a CallExpression's callee node is `parseInt` or
  20. * `Number.parseInt`.
  21. * @param {ASTNode} calleeNode The callee node to evaluate.
  22. * @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
  23. * false otherwise.
  24. */
  25. function isParseInt(calleeNode) {
  26. return (
  27. astUtils.isSpecificId(calleeNode, "parseInt") ||
  28. astUtils.isSpecificMemberAccess(calleeNode, "Number", "parseInt")
  29. );
  30. }
  31. //------------------------------------------------------------------------------
  32. // Rule Definition
  33. //------------------------------------------------------------------------------
  34. module.exports = {
  35. meta: {
  36. type: "suggestion",
  37. docs: {
  38. description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
  39. category: "ECMAScript 6",
  40. recommended: false,
  41. url: "https://eslint.org/docs/rules/prefer-numeric-literals"
  42. },
  43. schema: [],
  44. messages: {
  45. useLiteral: "Use {{system}} literals instead of {{functionName}}()."
  46. },
  47. fixable: "code"
  48. },
  49. create(context) {
  50. const sourceCode = context.getSourceCode();
  51. //----------------------------------------------------------------------
  52. // Public
  53. //----------------------------------------------------------------------
  54. return {
  55. "CallExpression[arguments.length=2]"(node) {
  56. const [strNode, radixNode] = node.arguments,
  57. str = astUtils.getStaticStringValue(strNode),
  58. radix = radixNode.value;
  59. if (
  60. str !== null &&
  61. astUtils.isStringLiteral(strNode) &&
  62. radixNode.type === "Literal" &&
  63. typeof radix === "number" &&
  64. radixMap.has(radix) &&
  65. isParseInt(node.callee)
  66. ) {
  67. const { system, literalPrefix } = radixMap.get(radix);
  68. context.report({
  69. node,
  70. messageId: "useLiteral",
  71. data: {
  72. system,
  73. functionName: sourceCode.getText(node.callee)
  74. },
  75. fix(fixer) {
  76. if (sourceCode.getCommentsInside(node).length) {
  77. return null;
  78. }
  79. const replacement = `${literalPrefix}${str}`;
  80. if (+replacement !== parseInt(str, radix)) {
  81. /*
  82. * If the newly-produced literal would be invalid, (e.g. 0b1234),
  83. * or it would yield an incorrect parseInt result for some other reason, don't make a fix.
  84. */
  85. return null;
  86. }
  87. const tokenBefore = sourceCode.getTokenBefore(node),
  88. tokenAfter = sourceCode.getTokenAfter(node);
  89. let prefix = "",
  90. suffix = "";
  91. if (
  92. tokenBefore &&
  93. tokenBefore.range[1] === node.range[0] &&
  94. !astUtils.canTokensBeAdjacent(tokenBefore, replacement)
  95. ) {
  96. prefix = " ";
  97. }
  98. if (
  99. tokenAfter &&
  100. node.range[1] === tokenAfter.range[0] &&
  101. !astUtils.canTokensBeAdjacent(replacement, tokenAfter)
  102. ) {
  103. suffix = " ";
  104. }
  105. return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);
  106. }
  107. });
  108. }
  109. }
  110. };
  111. }
  112. };