From 082a4eba2effc0d09e3b0806794d80800b52f7b2 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Sat, 11 Dec 2021 09:44:07 +0530 Subject: [PATCH 1/7] fix: improve autofix of `prefer-object-has-own` --- lib/rules/prefer-object-has-own.js | 7 +++++++ tests/lib/rules/prefer-object-has-own.js | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index dd21e95d47e..0359141b955 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -87,6 +87,13 @@ module.exports = { return null; } + const tokenJustBeforeNode = sourceCode.getTokensBefore(node, 1)[0]; + + // for https://github.com/eslint/eslint/pull/15346#issuecomment-991417335 + if (tokenJustBeforeNode && tokenJustBeforeNode.type === "Keyword" && !sourceCode.isSpaceBetween(node, tokenJustBeforeNode)) { + return fixer.replaceText(node.callee, " Object.hasOwn"); + } + return fixer.replaceText(node.callee, "Object.hasOwn"); } }); diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index a34cf774555..0f14497ca9b 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -224,6 +224,17 @@ ruleTester.run("prefer-object-has-own", rule, { endColumn: 61 }] }, + { + code: "const hasProperty={}.hasOwnProperty.call(object, property);", + output: "const hasProperty=Object.hasOwn(object, property);", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 19, + endLine: 1, + endColumn: 59 + }] + }, { code: "const hasProperty = (( {}.hasOwnProperty.call(object, property) ));", output: "const hasProperty = (( Object.hasOwn(object, property) ));", @@ -279,6 +290,19 @@ ruleTester.run("prefer-object-has-own", rule, { endColumn: 63 }] }, + + // https://github.com/eslint/eslint/pull/15346#issuecomment-991417335 + { + code: "function foo(){return{}.hasOwnProperty.call(object, property)}", + output: "function foo(){return Object.hasOwn(object, property)}", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 22, + endLine: 1, + endColumn: 62 + }] + }, { code: "Object['prototype']['hasOwnProperty']['call'](object, property);", output: "Object.hasOwn(object, property);", From c7367b5599b7de95afa1808634e9f092ee68179c Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Sat, 11 Dec 2021 13:45:52 +0530 Subject: [PATCH 2/7] fix: cover more cases --- lib/rules/prefer-object-has-own.js | 28 ++++++++++++++++++-- tests/lib/rules/prefer-object-has-own.js | 33 ++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 0359141b955..3bb0f99d5df 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -12,6 +12,10 @@ const astUtils = require("./utils/ast-utils"); +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + /** * Checks if the given node is considered to be an access to a property of `Object.prototype`. * @param {ASTNode} node `MemberExpression` node to evaluate. @@ -36,6 +40,26 @@ function hasLeftHandObject(node) { return false; } +/** + * Checks if the given token is `await` Identefier, `of` Itentifier or a keyword token + * Inspired from eslint-plugin-unicorn/rules/fix/fix-space-around-keywords.js + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is is `await` Identefier, `of` Itentifier or a keyword token + */ +function isProblematicToken(token) { + const { type, value } = token; + + if ( + (type === "Keyword" && /^[a-z]*$/u.test(value)) || + + // AwaitExpression or ForOfStatement + (type === "Identifier" && (value === "of" || value === "await"))) { + return true; + } + + return false; +} + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -87,10 +111,10 @@ module.exports = { return null; } - const tokenJustBeforeNode = sourceCode.getTokensBefore(node, 1)[0]; + const tokenJustBeforeNode = sourceCode.getTokenBefore(node, { includeComments: true }); // for https://github.com/eslint/eslint/pull/15346#issuecomment-991417335 - if (tokenJustBeforeNode && tokenJustBeforeNode.type === "Keyword" && !sourceCode.isSpaceBetween(node, tokenJustBeforeNode)) { + if (tokenJustBeforeNode && isProblematicToken(tokenJustBeforeNode) && !sourceCode.isSpaceBetween(node, tokenJustBeforeNode)) { return fixer.replaceText(node.callee, " Object.hasOwn"); } diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 0f14497ca9b..ff26868870d 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -303,6 +303,39 @@ ruleTester.run("prefer-object-has-own", rule, { endColumn: 62 }] }, + { + code: "function foo(){return/*comment*/{}.hasOwnProperty.call(object, property)}", + output: "function foo(){return/*comment*/Object.hasOwn(object, property)}", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 33, + endLine: 1, + endColumn: 73 + }] + }, + { + code: "async function foo(){return await{}.hasOwnProperty.call(object, property)}", + output: "async function foo(){return await Object.hasOwn(object, property)}", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 34, + endLine: 1, + endColumn: 74 + }] + }, + { + code: "async function foo(){return await/*comment*/{}.hasOwnProperty.call(object, property)}", + output: "async function foo(){return await/*comment*/Object.hasOwn(object, property)}", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 45, + endLine: 1, + endColumn: 85 + }] + }, { code: "Object['prototype']['hasOwnProperty']['call'](object, property);", output: "Object.hasOwn(object, property);", From 95f0084d1868d80bfd4514565de9bac3ad7f606a Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Sat, 11 Dec 2021 16:43:50 +0530 Subject: [PATCH 3/7] test: add more cases --- tests/lib/rules/prefer-object-has-own.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index ff26868870d..814967d985d 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -336,6 +336,28 @@ ruleTester.run("prefer-object-has-own", rule, { endColumn: 85 }] }, + { + code: "for (const x of{}.hasOwnProperty.call(object, property).toString());", + output: "for (const x of Object.hasOwn(object, property).toString());", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 16, + endLine: 1, + endColumn: 56 + }] + }, + { + code: "for (const x of/*comment*/{}.hasOwnProperty.call(object, property).toString());", + output: "for (const x of/*comment*/Object.hasOwn(object, property).toString());", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 27, + endLine: 1, + endColumn: 67 + }] + }, { code: "Object['prototype']['hasOwnProperty']['call'](object, property);", output: "Object.hasOwn(object, property);", From 7e0c12d777fab73944594d1a0d9ac6319fc68a5a Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Sat, 11 Dec 2021 16:54:01 +0530 Subject: [PATCH 4/7] test: add more test cases --- tests/lib/rules/prefer-object-has-own.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 814967d985d..64b34eafeed 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -358,6 +358,28 @@ ruleTester.run("prefer-object-has-own", rule, { endColumn: 67 }] }, + { + code: "for (const x in{}.hasOwnProperty.call(object, property).toString());", + output: "for (const x in Object.hasOwn(object, property).toString());", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 16, + endLine: 1, + endColumn: 56 + }] + }, + { + code: "for (const x in/*comment*/{}.hasOwnProperty.call(object, property).toString());", + output: "for (const x in/*comment*/Object.hasOwn(object, property).toString());", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 27, + endLine: 1, + endColumn: 67 + }] + }, { code: "Object['prototype']['hasOwnProperty']['call'](object, property);", output: "Object.hasOwn(object, property);", From 95e7cd3efe88b989b7640004c5232db298e5dac3 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Mon, 13 Dec 2021 06:47:22 +0530 Subject: [PATCH 5/7] fix: avoid unnecessary space in autofix output --- lib/rules/prefer-object-has-own.js | 24 ++---------------------- tests/lib/rules/prefer-object-has-own.js | 11 +++++++++++ 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 3bb0f99d5df..b21f98ee500 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -40,26 +40,6 @@ function hasLeftHandObject(node) { return false; } -/** - * Checks if the given token is `await` Identefier, `of` Itentifier or a keyword token - * Inspired from eslint-plugin-unicorn/rules/fix/fix-space-around-keywords.js - * @param {Token} token The token to check. - * @returns {boolean} `true` if the token is is `await` Identefier, `of` Itentifier or a keyword token - */ -function isProblematicToken(token) { - const { type, value } = token; - - if ( - (type === "Keyword" && /^[a-z]*$/u.test(value)) || - - // AwaitExpression or ForOfStatement - (type === "Identifier" && (value === "of" || value === "await"))) { - return true; - } - - return false; -} - //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -111,10 +91,10 @@ module.exports = { return null; } - const tokenJustBeforeNode = sourceCode.getTokenBefore(node, { includeComments: true }); + const tokenJustBeforeNode = sourceCode.getTokenBefore(node.callee, { includeComments: true }); // for https://github.com/eslint/eslint/pull/15346#issuecomment-991417335 - if (tokenJustBeforeNode && isProblematicToken(tokenJustBeforeNode) && !sourceCode.isSpaceBetween(node, tokenJustBeforeNode)) { + if (tokenJustBeforeNode && !astUtils.canTokensBeAdjacent(tokenJustBeforeNode, "Object.hasOwn") && !sourceCode.isSpaceBetween(node.callee, tokenJustBeforeNode)) { return fixer.replaceText(node.callee, " Object.hasOwn"); } diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 64b34eafeed..d58f7f5c211 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -380,6 +380,17 @@ ruleTester.run("prefer-object-has-own", rule, { endColumn: 67 }] }, + { + code: "function foo(){return({}.hasOwnProperty.call)(object, property)}", + output: "function foo(){return(Object.hasOwn)(object, property)}", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 22, + endLine: 1, + endColumn: 64 + }] + }, { code: "Object['prototype']['hasOwnProperty']['call'](object, property);", output: "Object.hasOwn(object, property);", From 46ab8595022f623dd59485f21fe664f67ebbc3f6 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Mon, 13 Dec 2021 06:54:11 +0530 Subject: [PATCH 6/7] refactor: code --- lib/rules/prefer-object-has-own.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index b21f98ee500..5664f13343f 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -94,7 +94,11 @@ module.exports = { const tokenJustBeforeNode = sourceCode.getTokenBefore(node.callee, { includeComments: true }); // for https://github.com/eslint/eslint/pull/15346#issuecomment-991417335 - if (tokenJustBeforeNode && !astUtils.canTokensBeAdjacent(tokenJustBeforeNode, "Object.hasOwn") && !sourceCode.isSpaceBetween(node.callee, tokenJustBeforeNode)) { + if ( + tokenJustBeforeNode && + !astUtils.canTokensBeAdjacent(tokenJustBeforeNode, "Object.hasOwn") && + !sourceCode.isSpaceBetween(node.callee, tokenJustBeforeNode) + ) { return fixer.replaceText(node.callee, " Object.hasOwn"); } From 804a8f9838df8c39923c307ee622d46056b20251 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Tue, 14 Dec 2021 06:43:08 +0530 Subject: [PATCH 7/7] refactor: apply suggestion --- lib/rules/prefer-object-has-own.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 5664f13343f..19abdb31565 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -96,8 +96,8 @@ module.exports = { // for https://github.com/eslint/eslint/pull/15346#issuecomment-991417335 if ( tokenJustBeforeNode && - !astUtils.canTokensBeAdjacent(tokenJustBeforeNode, "Object.hasOwn") && - !sourceCode.isSpaceBetween(node.callee, tokenJustBeforeNode) + tokenJustBeforeNode.range[1] === node.callee.range[0] && + !astUtils.canTokensBeAdjacent(tokenJustBeforeNode, "Object.hasOwn") ) { return fixer.replaceText(node.callee, " Object.hasOwn"); }