diff --git a/.eslintrc.yml b/.eslintrc.yml index 743225ce4..d2436dc52 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -121,6 +121,7 @@ overrides: prettier-internal-rules/flat-ast-path-call: error prettier-internal-rules/no-doc-builder-concat: error prettier-internal-rules/no-empty-flat-contents-for-if-break: error + prettier-internal-rules/no-unnecessary-ast-path-call: error prettier-internal-rules/prefer-ast-path-each: error prettier-internal-rules/prefer-indent-if-break: error prettier-internal-rules/prefer-is-non-empty-array: error diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/index.js b/scripts/tools/eslint-plugin-prettier-internal-rules/index.js index 37cf78b1e..9989800f6 100644 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/index.js +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/index.js @@ -10,6 +10,7 @@ module.exports = { "no-empty-flat-contents-for-if-break": require("./no-empty-flat-contents-for-if-break"), "no-identifier-n": require("./no-identifier-n"), "no-node-comments": require("./no-node-comments"), + "no-unnecessary-ast-path-call": require("./no-unnecessary-ast-path-call"), "prefer-ast-path-each": require("./prefer-ast-path-each"), "prefer-indent-if-break": require("./prefer-indent-if-break"), "prefer-is-non-empty-array": require("./prefer-is-non-empty-array"), diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/no-unnecessary-ast-path-call.js b/scripts/tools/eslint-plugin-prettier-internal-rules/no-unnecessary-ast-path-call.js new file mode 100644 index 000000000..4596e9891 --- /dev/null +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/no-unnecessary-ast-path-call.js @@ -0,0 +1,57 @@ +"use strict"; + +const selector = [ + "CallExpression", + "[optional=false]", + '[callee.type="MemberExpression"]', + "[callee.computed=false]", + "[callee.optional=false]", + '[callee.property.type="Identifier"]', + '[callee.property.name="call"]', + "[arguments.length=1]", + '[arguments.0.type!="SpreadElement"]', +].join(""); + +const messageId = "no-unnecessary-ast-path-call"; + +module.exports = { + meta: { + type: "suggestion", + docs: { + url: + "https://github.com/prettier/prettier/blob/main/scripts/eslint-plugin-prettier-internal-rules/no-unnecessary-ast-path-call.js", + }, + messages: { + [messageId]: "Do not use `AstPath.call()` with one argument.", + }, + fixable: "code", + }, + create(context) { + const sourceCode = context.getSourceCode(); + + return { + [selector](node) { + const problem = { + node, + messageId, + }; + + const [callback] = node.arguments; + + // Don't fix to IIFE + if ( + callback.type !== "ArrowFunctionExpression" && + callback.type !== "FunctionExpression" + ) { + problem.fix = function (fixer) { + const callbackText = sourceCode.getText(callback); + const astPathText = sourceCode.getText(node.callee.object); + return fixer.replaceText(node, `${callbackText}(${astPathText})`); + }; + } + + context.report(problem); + }, + }; + }, +}; diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/test.js b/scripts/tools/eslint-plugin-prettier-internal-rules/test.js index 12d778a05..6dfd77e86 100644 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/test.js +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/test.js @@ -367,3 +367,28 @@ test("no-empty-flat-contents-for-if-break", { }, ], }); + +test("no-unnecessary-ast-path-call", { + valid: [ + "call(foo)", + 'foo["call"](bar)', + "foo.call?.(bar)", + "foo?.call(bar)", + "foo.call(bar, name)", + "foo.notCall(bar)", + "foo.call(...bar)", + "foo.call()", + ], + invalid: [ + { + code: "foo.call(bar)", + output: "bar(foo)", + errors: 1, + }, + { + code: "foo.call(() => bar)", + output: "foo.call(() => bar)", + errors: 1, + }, + ], +}); diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js index 971bd3160..9465784ac 100644 --- a/src/language-css/printer-postcss.js +++ b/src/language-css/printer-postcss.js @@ -311,7 +311,7 @@ function genericPrint(path, options, print) { if (node.type === "media-query" && node.value === "") { return; } - parts.push(childPath.call(print)); + parts.push(print(childPath)); }, "nodes"); return group(indent(join(line, parts))); @@ -988,7 +988,7 @@ function printNodeSequence(path, options, print) { options.originalText.slice(locStart(childNode), locEnd(childNode)) ); } else { - parts.push(pathChild.call(print)); + parts.push(print(pathChild)); } if (i !== nodes.length - 1) { diff --git a/src/language-graphql/printer-graphql.js b/src/language-graphql/printer-graphql.js index 8ecf9e6e1..a8bee421f 100644 --- a/src/language-graphql/printer-graphql.js +++ b/src/language-graphql/printer-graphql.js @@ -21,7 +21,7 @@ function genericPrint(path, options, print) { case "Document": { const parts = []; path.each((pathChild, index, definitions) => { - parts.push(pathChild.call(print)); + parts.push(print(pathChild)); if (index !== definitions.length - 1) { parts.push(hardline); if ( diff --git a/src/language-js/print/binaryish.js b/src/language-js/print/binaryish.js index 2554970dc..01848d327 100644 --- a/src/language-js/print/binaryish.js +++ b/src/language-js/print/binaryish.js @@ -285,7 +285,7 @@ function printBinaryishExpressions( } } else { // Our stopping case. Simply print the node normally. - parts.push(group(path.call(print))); + parts.push(group(print(path))); } return parts; diff --git a/src/language-js/print/call-arguments.js b/src/language-js/print/call-arguments.js index c843bf5ee..77e5aa492 100644 --- a/src/language-js/print/call-arguments.js +++ b/src/language-js/print/call-arguments.js @@ -150,7 +150,7 @@ function printCallArguments(path, options, print) { if (shouldGroupFirst && i === 0) { printedExpanded = [ [ - argPath.call((p) => print(p, { expandFirstArg: true })), + print(argPath, { expandFirstArg: true }), printedArguments.length > 1 ? "," : "", hasEmptyLineFollowingFirstArg ? hardline : line, hasEmptyLineFollowingFirstArg ? hardline : "", @@ -161,7 +161,7 @@ function printCallArguments(path, options, print) { if (shouldGroupLast && i === args.length - 1) { printedExpanded = [ ...printedArguments.slice(0, -1), - argPath.call((p) => print(p, { expandLastArg: true })), + print(argPath, { expandLastArg: true }), ]; } }); diff --git a/src/language-js/print/function-parameters.js b/src/language-js/print/function-parameters.js index 7a5b0150d..d8a6e3c17 100644 --- a/src/language-js/print/function-parameters.js +++ b/src/language-js/print/function-parameters.js @@ -65,7 +65,7 @@ function printFunctionParameters( if (isLastParameter && functionNode.rest) { printed.push("..."); } - printed.push(parameterPath.call(print)); + printed.push(print(parameterPath)); if (isLastParameter) { return; } diff --git a/src/language-js/print/function.js b/src/language-js/print/function.js index 6240c7e5c..27542fea4 100644 --- a/src/language-js/print/function.js +++ b/src/language-js/print/function.js @@ -250,7 +250,7 @@ function printArrowFunctionExpression(path, options, print, args) { let body; let chainShouldBreak = false; - path.call(function rec(path) { + (function rec(path) { const doc = printArrowFunctionSignature(path, options, print, args); signatures.push( signatures.length === 0 ? doc : printComments(path, doc, options) @@ -272,7 +272,7 @@ function printArrowFunctionExpression(path, options, print, args) { node = node.body; path.call(rec, "body"); } - }); + })(path); if (signatures.length > 1) { return printArrowChain( diff --git a/src/language-js/print/member-chain.js b/src/language-js/print/member-chain.js index 81e9f565a..43963f394 100644 --- a/src/language-js/print/member-chain.js +++ b/src/language-js/print/member-chain.js @@ -135,7 +135,7 @@ function printMemberChain(path, options, print) { } else { printedNodes.unshift({ node, - printed: path.call(print), + printed: print(path), }); } } diff --git a/src/language-js/print/type-annotation.js b/src/language-js/print/type-annotation.js index 5761d9ee8..0b8972dfa 100644 --- a/src/language-js/print/type-annotation.js +++ b/src/language-js/print/type-annotation.js @@ -169,7 +169,7 @@ function printUnionType(path, options, print) { // // comment // | child2 const printed = path.map((typePath) => { - let printedType = typePath.call(print); + let printedType = print(typePath); if (!shouldHug) { printedType = align(2, printedType); } diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index 7fb5c6905..29ab70c91 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -702,7 +702,7 @@ function printPathNoParens(path, options, print, args) { path.map((casePath, index, cases) => { const caseNode = casePath.getValue(); return [ - casePath.call(print), + print(casePath), index !== cases.length - 1 && isNextLineEmpty(caseNode, options) ? hardline diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index b67adb16a..b1f2f8d06 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -411,8 +411,8 @@ function genericPrint(path, options, print) { printChildren(path, options, print, { processor: (childPath, index) => index === 0 - ? group([softline, childPath.call(print)]) - : childPath.call(print), + ? group([softline, print(childPath)]) + : print(childPath), }) ), nextNode && nextNode.type === "footnoteDefinition" @@ -469,13 +469,13 @@ function printListItem(path, options, print, listPrefix) { printChildren(path, options, print, { processor: (childPath, index) => { if (index === 0 && childPath.getValue().type !== "list") { - return align(" ".repeat(prefix.length), childPath.call(print)); + return align(" ".repeat(prefix.length), print(childPath)); } const alignment = " ".repeat( clamp(options.tabWidth - listPrefix.length, 0, 3) // 4+ will cause indented code block ); - return [alignment, align(alignment, childPath.call(print))]; + return [alignment, align(alignment, print(childPath))]; }, }), ]; @@ -565,7 +565,7 @@ function printTable(path, options, print) { const contents = path.map( (rowPath) => rowPath.map((cellPath, columnIndex) => { - const text = printDocToString(cellPath.call(print), options).formatted; + const text = printDocToString(print(cellPath), options).formatted; const width = getStringWidth(text); columnMaxWidths[columnIndex] = Math.max( columnMaxWidths[columnIndex] || 3, // minimum width = 3 (---, :--, :-:, --:) @@ -691,14 +691,14 @@ function printRoot(path, options, print) { } } - return childPath.call(print); + return print(childPath); }, }); } function printChildren(path, options, print, events = {}) { const { postprocessor } = events; - const processor = events.processor || ((childPath) => childPath.call(print)); + const processor = events.processor || ((childPath) => print(childPath)); const node = path.getValue(); const parts = [];