Repository: eslint/eslint
File: lib/rules/no-prototype-builtins.js (JavaScript)

Code before:
        /**
         * Reports if a disallowed property is used in a CallExpression
         * @param {ASTNode} node The CallExpression node.
         * @returns {void}
         */
        function disallowBuiltIns(node) {

            const callee = astUtils.skipChainExpression(node.callee);

            if (callee.type !== "MemberExpression") {
                return;
            }

            const propName = astUtils.getStaticPropertyName(callee);

            if (propName !== null && DISALLOWED_PROPS.has(propName)) {
                context.report({
                    messageId: "prototypeBuildIn",
                    loc: callee.property.loc,
                    data: { prop: propName },
                    node,
                    suggest: [
                        {
                            messageId: "callObjectPrototype",
                            data: { prop: propName },
                            fix(fixer) {

                                /*
                                 * a call after an optional chain (e.g. a?.b.hasOwnProperty(c))
                                 * must be fixed manually because the call can be short-circuited
                                 */
                                if (isAfterOptional(node)) {
                                    return null;
                                }

                                const sourceCode = context.sourceCode;
                                const openParenToken = sourceCode.getTokenAfter(
                                    node.callee,
                                    astUtils.isOpeningParenToken
                                );
                                const objectText = sourceCode.getText(callee.object);
                                const isEmptyParameters = node.arguments.length === 0;
                                const delim = isEmptyParameters ? "" : ", ";
                                const fixes = [
                                    fixer.replaceText(callee, `Object.prototype.${propName}.call`),
                                    fixer.insertTextAfter(openParenToken, objectText + delim)
                                ];

                                return fixes;
                            }
                        }

Review comment (bug):
If the global variable `Object` is shadowed, suggestion that references `Object` would be wrong.

```js
const Object = {};

foo.hasOwnProperty('bar');
```

```js
function f(Object) {
    foo.hasOwnProperty('bar');
}

```

To prevent providing suggestion if global `Object` is shadowed (or for whatever reason turned off):

```js
const objectVariable = astUtils.getVariableByName(sourceCode.getScope(node), "Object");

if (!objectVariable || objectVariable.scope.type !== "global" || objectVariable.defs.length > 0) {
    return null;
}
```

Code after:
        /**
         * Reports if a disallowed property is used in a CallExpression
         * @param {ASTNode} node The CallExpression node.
         * @returns {void}
         */
        function disallowBuiltIns(node) {

            const callee = astUtils.skipChainExpression(node.callee);

            if (callee.type !== "MemberExpression") {
                return;
            }

            const propName = astUtils.getStaticPropertyName(callee);

            if (propName !== null && DISALLOWED_PROPS.has(propName)) {
                context.report({
                    messageId: "prototypeBuildIn",
                    loc: callee.property.loc,
                    data: { prop: propName },
                    node,
                    suggest: [
                        {
                            messageId: "callObjectPrototype",
                            data: { prop: propName },
                            fix(fixer) {
                                const sourceCode = context.sourceCode;

                                /*
                                 * A call after an optional chain (e.g. a?.b.hasOwnProperty(c))
                                 * must be fixed manually because the call can be short-circuited
                                 */
                                if (isAfterOptional(node)) {
                                    return null;
                                }

                                /*
                                 * A call on a ChainExpression (e.g. (a?.hasOwnProperty)(c)) will trigger
                                 * no-unsafe-optional-chaining which should be fixed before this suggestion
                                 */
                                if (node.callee.type === "ChainExpression") {
                                    return null;
                                }

                                const objectVariable = astUtils.getVariableByName(sourceCode.getScope(node), "Object");

                                /*
                                 * We can't use Object if the global Object was shadowed,
                                 * or Object does not exist in the global scope for some reason
                                 */
                                if (!objectVariable || objectVariable.scope.type !== "global" || objectVariable.defs.length > 0) {