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