no-obj-calls.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /**
  2. * @fileoverview Rule to flag use of an object property of the global object (Math and JSON) as a function
  3. * @author James Allardice
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const {
  10. CALL,
  11. CONSTRUCT,
  12. ReferenceTracker,
  13. } = require("@eslint-community/eslint-utils");
  14. const getPropertyName = require("./utils/ast-utils").getStaticPropertyName;
  15. //------------------------------------------------------------------------------
  16. // Helpers
  17. //------------------------------------------------------------------------------
  18. const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect", "Intl"];
  19. /**
  20. * Returns the name of the node to report
  21. * @param {ASTNode} node A node to report
  22. * @returns {string} name to report
  23. */
  24. function getReportNodeName(node) {
  25. if (node.type === "ChainExpression") {
  26. return getReportNodeName(node.expression);
  27. }
  28. if (node.type === "MemberExpression") {
  29. return getPropertyName(node);
  30. }
  31. return node.name;
  32. }
  33. //------------------------------------------------------------------------------
  34. // Rule Definition
  35. //------------------------------------------------------------------------------
  36. /** @type {import('../types').Rule.RuleModule} */
  37. module.exports = {
  38. meta: {
  39. type: "problem",
  40. docs: {
  41. description:
  42. "Disallow calling global object properties as functions",
  43. recommended: true,
  44. url: "https://eslint.org/docs/latest/rules/no-obj-calls",
  45. },
  46. schema: [],
  47. messages: {
  48. unexpectedCall: "'{{name}}' is not a function.",
  49. unexpectedRefCall:
  50. "'{{name}}' is reference to '{{ref}}', which is not a function.",
  51. },
  52. },
  53. create(context) {
  54. const sourceCode = context.sourceCode;
  55. return {
  56. Program(node) {
  57. const scope = sourceCode.getScope(node);
  58. const tracker = new ReferenceTracker(scope);
  59. const traceMap = {};
  60. for (const g of nonCallableGlobals) {
  61. traceMap[g] = {
  62. [CALL]: true,
  63. [CONSTRUCT]: true,
  64. };
  65. }
  66. for (const {
  67. node: refNode,
  68. path,
  69. } of tracker.iterateGlobalReferences(traceMap)) {
  70. const name = getReportNodeName(refNode.callee);
  71. const ref = path[0];
  72. const messageId =
  73. name === ref ? "unexpectedCall" : "unexpectedRefCall";
  74. context.report({
  75. node: refNode,
  76. messageId,
  77. data: { name, ref },
  78. });
  79. }
  80. },
  81. };
  82. },
  83. };