• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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