From 9a7dbe788c4fdc791533438f318816222b0c395c Mon Sep 17 00:00:00 2001 From: Jordan Kiesel Date: Sun, 21 Jun 2026 19:47:48 -0600 Subject: [PATCH] feat: align parentheses closer with Prettier JS/TS, including their removal --- scripts/generate-node-types.ts | 17 +- src/comments.ts | 10 +- src/node-types.ts | 10 +- src/parser.ts | 88 +-- src/printer.ts | 13 +- src/printers/blocks-and-statements.ts | 82 ++- src/printers/expressions.ts | 500 +++++++++++------- src/printers/helpers.ts | 328 +++++++++++- test/unit-test/assert/_output.java | 4 +- .../operator-position-end/_output.java | 4 +- .../operator-position-start/_output.java | 4 +- test/unit-test/cast/_output.java | 12 +- test/unit-test/comments/class/_output.java | 12 +- .../comments/expression/_output.java | 2 +- .../spaces/_output.java | 12 +- .../conditional-expression/tabs/_output.java | 4 +- test/unit-test/expressions/_input.java | 14 - test/unit-test/expressions/_output.java | 25 - .../lambda/arrow-parens-always/_output.java | 4 +- .../lambda/arrow-parens-avoid/_output.java | 4 +- test/unit-test/yield-statement/_output.java | 2 +- 21 files changed, 802 insertions(+), 349 deletions(-) diff --git a/scripts/generate-node-types.ts b/scripts/generate-node-types.ts index afc27bfe0..954b1427c 100644 --- a/scripts/generate-node-types.ts +++ b/scripts/generate-node-types.ts @@ -110,18 +110,25 @@ ${ ? `${Object.entries(fields as unknown as Record) .map(([field, children]) => { let fieldName = `${field}Node`; - let type = children.types.length - ? children.types.map(t => getTypeExprFromRef(t)).join(" | ") + let fieldType = children.types.length + ? children.types + .map(fieldType => + fieldType.type === "primary_expression" + ? { type: "expression", named: true } + : fieldType + ) + .map(getTypeExprFromRef) + .join(" | ") : "UnnamedNode"; if (children.multiple) { if (children.types.length > 1) { - type = `(${type})`; + fieldType = `(${fieldType})`; } - type += "[]"; + fieldType += "[]"; fieldName += "s"; } const opt = children.required || children.multiple ? "" : "?"; - return ` ${fieldName}${opt}: ${type};`; + return ` ${fieldName}${opt}: ${fieldType};`; }) .join("\n")} }` diff --git a/src/comments.ts b/src/comments.ts index 9fab3228f..52c19a7e4 100644 --- a/src/comments.ts +++ b/src/comments.ts @@ -12,6 +12,7 @@ import parser from "./parser.ts"; import printer from "./printer.ts"; import { hasChild, + isMember, printComment, type JavaParserOptions, type NamedNodePath @@ -243,7 +244,6 @@ function handleMemberChainComments(commentNode: CommentNode) { util.addLeadingComment(enclosingNode, commentNode); return true; } else if ( - followingNode && isMember(followingNode) && (!precedingNode || (precedingNode !== getMemberObject(followingNode) && @@ -329,14 +329,6 @@ function handleTryStatementComments(commentNode: CommentNode) { return false; } -function isMember(node: SyntaxNode) { - return ( - node.type === SyntaxType.ArrayAccess || - node.type === SyntaxType.FieldAccess || - node.type === SyntaxType.MethodInvocation - ); -} - function getMemberObject( node: ArrayAccessNode | FieldAccessNode | MethodInvocationNode ) { diff --git a/src/node-types.ts b/src/node-types.ts index 31dee7c8c..e59803a39 100644 --- a/src/node-types.ts +++ b/src/node-types.ts @@ -740,7 +740,7 @@ export interface ArgumentListNode extends NamedNodeBase { export interface ArrayAccessNode extends NamedNodeBase { type: SyntaxType.ArrayAccess; - arrayNode: PrimaryExpressionNode; + arrayNode: ExpressionNode; indexNode: ExpressionNode; } @@ -952,7 +952,7 @@ export interface ExplicitConstructorInvocationNode extends NamedNodeBase { type: SyntaxType.ExplicitConstructorInvocation; argumentsNode: ArgumentListNode; constructorNode: SuperNode | ThisNode; - objectNode?: PrimaryExpressionNode; + objectNode?: ExpressionNode; type_argumentsNode?: TypeArgumentsNode; } @@ -973,7 +973,7 @@ export interface ExtendsInterfacesNode extends NamedNodeBase { export interface FieldAccessNode extends NamedNodeBase { type: SyntaxType.FieldAccess; fieldNode: IdentifierNode | ThisNode; - objectNode: PrimaryExpressionNode | SuperNode; + objectNode: ExpressionNode | SuperNode; } export interface FieldDeclarationNode extends NamedNodeBase { @@ -1094,7 +1094,7 @@ export interface MethodInvocationNode extends NamedNodeBase { type: SyntaxType.MethodInvocation; argumentsNode: ArgumentListNode; nameNode: IdentifierNode; - objectNode?: PrimaryExpressionNode | SuperNode; + objectNode?: ExpressionNode | SuperNode; type_argumentsNode?: TypeArgumentsNode; } @@ -1281,7 +1281,7 @@ export interface SynchronizedStatementNode extends NamedNodeBase { export interface TemplateExpressionNode extends NamedNodeBase { type: SyntaxType.TemplateExpression; template_argumentNode: StringLiteralNode; - template_processorNode: PrimaryExpressionNode; + template_processorNode: ExpressionNode; } export interface TernaryExpressionNode extends NamedNodeBase { diff --git a/src/parser.ts b/src/parser.ts index 8efb4b4d3..a805fa287 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -7,6 +7,7 @@ import { type CommentNode, type SyntaxNode } from "./node-types.ts"; +import { createTypeCheckFunction } from "./printers/helpers.ts"; export default { async parse(text) { @@ -48,6 +49,16 @@ const parser = (async () => { return parser; })(); +const isParenthesizedParent = createTypeCheckFunction([ + SyntaxType.DoStatement, + SyntaxType.IfStatement, + SyntaxType.SwitchExpression, + SyntaxType.SynchronizedStatement, + SyntaxType.TryStatement, + SyntaxType.TryWithResourcesStatement, + SyntaxType.WhileStatement +]); + function processTree( node: Node, fieldName: string | null = null, @@ -89,44 +100,51 @@ function processTree( Object.keys(multiFields).forEach(name => (javaNode[`${name}Nodes`] = [])); } - node.children.forEach((child, index) => { - const { type, text: value, startPosition, endPosition } = child; - if (type === SyntaxType.BlockComment || type === SyntaxType.LineComment) { - comments.push({ - type, - value, - start: { - index: child.startIndex, - row: startPosition.row, - column: startPosition.column - }, - end: { - index: child.endIndex, - row: endPosition.row, - column: endPosition.column - }, - leading: false, - trailing: false, - printed: false - }); - } else { - const fieldName = node.fieldNameForChild(index); - const javaChild = processTree(child, fieldName, comments); - - javaNode.children.push(javaChild); - if (javaChild.isNamed) { - javaNode.namedChildren.push(javaChild); - } + node.children + .flatMap(child => + child.type === SyntaxType.ParenthesizedExpression && + !isParenthesizedParent(javaNode) + ? child.namedChildren + : [child] + ) + .forEach((child, index) => { + const { type, text: value, startPosition, endPosition } = child; + if (type === SyntaxType.BlockComment || type === SyntaxType.LineComment) { + comments.push({ + type, + value, + start: { + index: child.startIndex, + row: startPosition.row, + column: startPosition.column + }, + end: { + index: child.endIndex, + row: endPosition.row, + column: endPosition.column + }, + leading: false, + trailing: false, + printed: false + }); + } else { + const fieldName = node.fieldNameForChild(index); + const javaChild = processTree(child, fieldName, comments); + + javaNode.children.push(javaChild); + if (javaChild.isNamed) { + javaNode.namedChildren.push(javaChild); + } - if (fieldName) { - if (multiFields?.[fieldName]) { - javaNode[`${fieldName}Nodes`].push(javaChild); - } else { - javaNode[`${fieldName}Node`] = javaChild; + if (fieldName) { + if (multiFields?.[fieldName]) { + javaNode[`${fieldName}Nodes`].push(javaChild); + } else { + javaNode[`${fieldName}Node`] = javaChild; + } } } - } - }); + }); return javaNode; } diff --git a/src/printer.ts b/src/printer.ts index 1029bdfa8..091cf9553 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -11,6 +11,7 @@ import { SyntaxType, type CommentNode, type SyntaxNode } from "./node-types.ts"; import { embedTextBlock, hasType, + needsParentheses, printComment, printValue, type NamedNodePath @@ -19,9 +20,13 @@ import { printerForNodeType } from "./printers/index.ts"; export default { print(path, options, print, args) { - return hasJavaNode(path) - ? printerForNodeType(path.node.type)(path, print, options, args) - : printValue(path); + if (!hasNamedNode(path)) { + return printValue(path); + } + + const doc = printerForNodeType(path.node.type)(path, print, options, args); + + return needsParentheses(path) ? ["(", doc, ")"] : doc; }, embed(path) { return hasType(path, SyntaxType.StringLiteral) @@ -56,6 +61,6 @@ export default { } } satisfies Printer; -function hasJavaNode(path: AstPath): path is NamedNodePath { +function hasNamedNode(path: AstPath): path is NamedNodePath { return path.node.isNamed; } diff --git a/src/printers/blocks-and-statements.ts b/src/printers/blocks-and-statements.ts index b8aad1a1e..8566534b8 100644 --- a/src/printers/blocks-and-statements.ts +++ b/src/printers/blocks-and-statements.ts @@ -1,6 +1,10 @@ -import type { Doc } from "prettier"; +import type { AstPath, Doc } from "prettier"; import { builders } from "prettier/doc"; -import { SyntaxType } from "../node-types.ts"; +import { + SyntaxType, + type ReturnStatementNode, + type ThrowStatementNode +} from "../node-types.ts"; import { hasChild, hasLeadingComments, @@ -10,7 +14,10 @@ import { printDanglingComments, printModifiers, printVariableDeclaration, - type NamedNodePrinters + returnArgumentHasLeadingComment, + type NamedNodePath, + type NamedNodePrinters, + type PrintFunction } from "./helpers.ts"; const { @@ -291,32 +298,8 @@ export default { : "continue;"; }, - return_statement(path, print) { - const statement: Doc[] = ["return"]; - - if (path.node.namedChildren.length) { - statement.push(" "); - const expression = path.call(print, "namedChildren", 0); - if (path.node.namedChildren[0].type === SyntaxType.BinaryExpression) { - statement.push( - group([ - ifBreak("("), - indent([softline, expression]), - softline, - ifBreak(")") - ]) - ); - } else { - statement.push(expression); - } - } - statement.push(";"); - return statement; - }, - - throw_statement(path, print) { - return ["throw ", path.call(print, "namedChildren", 0), ";"]; - }, + return_statement: printReturnOrThrowStatement, + throw_statement: printReturnOrThrowStatement, synchronized_statement(path, print) { const parenthesizedExpressionIndex = path.node.namedChildren.findIndex( @@ -483,3 +466,44 @@ function printExpressionList(expressions: Doc[]) { ) ); } + +function printReturnOrThrowArgument(path: NamedNodePath, print: PrintFunction) { + const { node } = path; + const argumentDoc = print(path); + + if (returnArgumentHasLeadingComment(node)) { + return ["(", indent([hardline, argumentDoc]), hardline, ")"]; + } + + if (node.type === SyntaxType.BinaryExpression) { + return group([ + ifBreak("("), + indent([softline, argumentDoc]), + softline, + ifBreak(")") + ]); + } + + return argumentDoc; +} + +function printReturnOrThrowStatement( + path: AstPath, + print: PrintFunction +) { + const { node } = path; + return [ + node.type === SyntaxType.ThrowStatement ? "throw" : "return", + node.namedChildren.length + ? [ + " ", + path.call( + () => printReturnOrThrowArgument(path, print), + "namedChildren", + 0 + ) + ] + : "", + ";" + ]; +} diff --git a/src/printers/expressions.ts b/src/printers/expressions.ts index 27c11971c..2ebf22fc4 100644 --- a/src/printers/expressions.ts +++ b/src/printers/expressions.ts @@ -1,13 +1,19 @@ -import { util, type Doc } from "prettier"; +import { util, type AstPath, type Doc } from "prettier"; import { builders, utils } from "prettier/doc"; import { printComments, printCommentsSeparately } from "../comments.ts"; -import { SyntaxType, type NamedNode } from "../node-types.ts"; +import { + SyntaxType, + type NamedNode, + type TernaryExpressionNode +} from "../node-types.ts"; import { hasChild, hasLeadingComments, hasType, indentInParentheses, + isReturnOrThrowStatement, printDanglingComments, + shouldFlatten, type JavaParserOptions, type NamedNodePath, type NamedNodePrinters, @@ -18,6 +24,7 @@ const { align, breakParent, conditionalGroup, + dedent, group, hardline, ifBreak, @@ -35,7 +42,7 @@ const { removeLines, willBreak } = utils; export default { lambda_expression(path, print, options, args) { const signatureDocs: Doc[] = []; - let bodyDoc: Doc | undefined; + let bodyDoc: Doc; const bodyComments: Doc[] = []; const shouldPrintAsChain = !( @@ -44,7 +51,7 @@ export default { "expandLastArg" in args && args.expandLastArg ) && path.node.bodyNode.type === SyntaxType.LambdaExpression; - let functionBody: typeof path.node.bodyNode | undefined; + let functionBody: typeof path.node.bodyNode; (function rec() { const { node } = path; @@ -77,61 +84,28 @@ export default { // as the arrow. const shouldPutBodyOnSameLine = !functionBody!.comments?.some( - ({ leading }) => - leading && hasNewline(options.originalText, functionBody!.end.index) + comment => + comment.leading && hasNewline(options.originalText, comment.end.index) ) && mayBreakAfterShortPrefix(functionBody!); - const isCallee = - path.node.fieldName === "object" && - (path.parent as NamedNode | null)?.type === SyntaxType.MethodInvocation; const chainGroupId = Symbol("arrow-chain"); const signaturesDoc = printArrowFunctionSignatures(path, { signatureDocs }); - let shouldBreakSignatures = false; - let shouldIndentSignatures = false; - let shouldPrintSoftlineInIndent = false; - if (shouldPrintAsChain && isCallee) { - shouldIndentSignatures = true; - // If the lambda expression has a leading line comment, there should be a - // hardline above it so we should not print a softline in indent call - shouldPrintSoftlineInIndent = !path.node.comments?.some( - ({ type, leading }) => leading && type === SyntaxType.LineComment - ); - shouldBreakSignatures = isCallee && !shouldPutBodyOnSameLine; - } - // if the arrow function is expanded as last argument, we are adding a - // level of indentation and need to add a softline to align the closing ) - // with the opening (. - const trailingSpace = - args && - typeof args === "object" && - "expandLastArg" in args && - args.expandLastArg && - !path.node.comments - ? softline - : ""; - - bodyDoc = shouldPutBodyOnSameLine - ? [" ", bodyDoc!, bodyComments] - : [indent([line, bodyDoc!, bodyComments]), trailingSpace]; + bodyDoc = printArrowFunctionBody(path, args, { + bodyDoc: bodyDoc!, + bodyComments, + shouldPutBodyOnSameLine + }); return group([ - group( - shouldIndentSignatures - ? indent([shouldPrintSoftlineInIndent ? softline : "", signaturesDoc]) - : signaturesDoc, - { shouldBreak: shouldBreakSignatures, id: chainGroupId } - ), + group(signaturesDoc, { id: chainGroupId }), " ->", shouldPrintAsChain ? indentIfBreak(bodyDoc, { groupId: chainGroupId }) - : group(bodyDoc), - shouldPrintAsChain && isCallee - ? ifBreak(softline, "", { groupId: chainGroupId }) - : "" + : group(bodyDoc) ]); }, @@ -157,33 +131,104 @@ export default { }, ternary_expression(path, print, options) { - const condition = path.call(print, "conditionNode"); - const consequence = path.call(print, "consequenceNode"); - const alternative = path.call(print, "alternativeNode"); + const { node } = path; + const consequentNode = node.consequenceNode; + const parts = []; + + const parent = path.parent as NamedNode; + const isParentTest = + parent.type === node.type && parent.conditionNode === node; + const forceNoIndent = parent.type === node.type && !isParentTest; + + // Find the outermost non-TernaryExpression parent. + let currentParent: NamedNode | null | undefined; + let previousParent: NamedNode; + let i = 0; + do { + previousParent = currentParent || node; + currentParent = path.getParentNode(i); + i++; + } while ( + currentParent && + currentParent.type === node.type && + currentParent.conditionNode !== previousParent + ); + const firstNonConditionalParent = currentParent || parent; + + /* + This does not mean to indent, but make the doc aligned with the first character after `? ` or `: `, + so we use `2` instead of `options.tabWidth` here. + + ```js + test + ? { + consequent + } + : alternate + ``` - const parentType = path.parent?.type; - const isNestedTernary = parentType === SyntaxType.TernaryExpression; - const suffix = [ + instead of + + ```js + test + ? { + consequent + } + : alternate + ``` + */ + const printBranch = ( + nodePropertyName: "consequenceNode" | "alternativeNode" + ) => + options.useTabs + ? indent(path.call(print, nodePropertyName)) + : align(2, path.call(print, nodePropertyName)); + const part = [ line, - ["? ", options.useTabs ? indent(consequence) : align(2, consequence)], + "? ", + consequentNode.type === node.type ? ifBreak("", "(") : "", + printBranch("consequenceNode"), + consequentNode.type === node.type ? ifBreak("", ")") : "", line, - [": ", options.useTabs ? indent(alternative) : align(2, alternative)] + ": ", + printBranch("alternativeNode") ]; + parts.push( + parent.type !== node.type || + parent.alternativeNode === node || + isParentTest + ? part + : options.useTabs + ? dedent(indent(part)) + : align(Math.max(0, options.tabWidth - 2), part) + ); - const prefix = group(condition); - const alignedSuffix = - !isNestedTernary || options.useTabs - ? suffix - : align(Math.max(0, options.tabWidth - 2), suffix); - - if (isNestedTernary) { - return [prefix, alignedSuffix]; - } + const maybeGroup = (doc: Doc) => + parent === firstNonConditionalParent ? group(doc) : doc; + + // Break the closing paren to keep the chain right after it: + // (a + // ? b + // : c + // ).call() + const breakClosingParen = + parent.type === SyntaxType.ExplicitConstructorInvocation || + parent.type === SyntaxType.FieldAccess || + parent.type === SyntaxType.MethodInvocation || + parent.type === SyntaxType.MethodReference || + parent.type === SyntaxType.ObjectCreationExpression; + + const shouldExtraIndent = shouldExtraIndentForTernaryExpression(path); + + const result = maybeGroup([ + printTernaryTest(path, print), + forceNoIndent ? parts : indent(parts), + breakClosingParen && !shouldExtraIndent ? softline : "" + ]); - const parts = [prefix, indent(alignedSuffix)]; - return parentType === SyntaxType.ParenthesizedExpression - ? parts - : group(parts); + return isParentTest || shouldExtraIndent + ? group([indent([softline, result]), softline]) + : result; }, assignment_expression(path, print) { @@ -214,13 +259,13 @@ export default { const parent = path.parent as NamedNode | null; const grandparent = path.grandparent as NamedNode | null; const isInsideParentheses = - (parent?.fieldName === "condition" && - (grandparent?.type === SyntaxType.IfStatement || - grandparent?.type === SyntaxType.WhileStatement || - grandparent?.type === SyntaxType.SwitchExpression || - grandparent?.type === SyntaxType.DoStatement)) || - (parent?.fieldName !== "body" && - grandparent?.type === SyntaxType.SynchronizedStatement); + ((grandparent?.type === SyntaxType.IfStatement || + grandparent?.type === SyntaxType.WhileStatement || + grandparent?.type === SyntaxType.SwitchExpression || + grandparent?.type === SyntaxType.DoStatement) && + grandparent.conditionNode === parent) || + (grandparent?.type === SyntaxType.SynchronizedStatement && + grandparent.bodyNode !== parent); const parts = printBinaryExpressions( path, @@ -229,39 +274,57 @@ export default { isInsideParentheses ); + // if ( + // this.hasPlugin("dynamicImports") && this.lookahead().type === tt.parenLeft + // ) { + // + // looks super weird, we want to break the children if the parent breaks + // + // if ( + // this.hasPlugin("dynamicImports") && + // this.lookahead().type === tt.parenLeft + // ) { if (isInsideParentheses) { return parts; } + // Break between the parens in + // unaries or in a member or specific call expression, i.e. + // + // ( + // a && + // b && + // c + // ).call() if ( - (parent?.fieldName === "object" && - (grandparent?.type === SyntaxType.MethodInvocation || - grandparent?.type === SyntaxType.ExplicitConstructorInvocation || - grandparent?.type === SyntaxType.FieldAccess)) || - grandparent?.type === SyntaxType.MethodReference + (parent?.type === SyntaxType.UnaryExpression && !node.comments) || + ((parent?.type === SyntaxType.ExplicitConstructorInvocation || + parent?.type === SyntaxType.FieldAccess || + parent?.type === SyntaxType.MethodInvocation) && + parent.objectNode === node) || + parent?.type === SyntaxType.MethodReference || + parent?.type === SyntaxType.ObjectCreationExpression ) { - return parts; + return group([indent([softline, ...parts]), softline]); } // Avoid indenting sub-expressions in some cases where the first sub-expression is already // indented accordingly. We should indent sub-expressions where the first case isn't indented. const shouldNotIndent = - parent?.type === SyntaxType.ReturnStatement || - parent?.type === SyntaxType.ThrowStatement || - parent?.type === SyntaxType.ParenthesizedExpression || + isReturnOrThrowStatement(parent) || parent?.type === SyntaxType.AssignmentExpression || parent?.type === SyntaxType.VariableDeclarator || parent?.type === SyntaxType.Guard || - (node.fieldName === "body" && - parent?.type === SyntaxType.LambdaExpression) || - (node.fieldName !== "body" && parent?.type === SyntaxType.ForStatement) || + (parent?.type === SyntaxType.LambdaExpression && + parent.bodyNode === node) || + (parent?.type === SyntaxType.ForStatement && + parent.conditionNode === node) || (parent?.type === SyntaxType.TernaryExpression && - grandparent?.type !== SyntaxType.ReturnStatement && - grandparent?.type !== SyntaxType.ThrowStatement && + !isReturnOrThrowStatement(grandparent) && grandparent?.type !== SyntaxType.ParenthesizedExpression && grandparent?.type !== SyntaxType.ArgumentList) || - (node.fieldName === "operand" && - parent?.type === SyntaxType.UnaryExpression); + (parent?.type === SyntaxType.UnaryExpression && + parent.operandNode === node); if (shouldNotIndent) { return group(parts); @@ -392,12 +455,39 @@ export default { }, cast_expression(path, print) { - const types = path.map(print, "typeNodes"); - const value = path.call(print, "valueNode"); + const { node } = path; + + const castGroup = group( + node.typeNodes.length === 1 + ? ["(", path.call(print, "typeNodes", 0), ")"] + : [ + "(", + indent([ + softline, + join([" &", line], path.map(print, "typeNodes")) + ]), + softline, + ")" + ] + ); + + const parts = [castGroup, " ", path.call(print, "valueNode")]; - return types.length > 1 - ? [group(indentInParentheses(join([line, "& "], types))), " ", value] - : ["(", ...types, ") ", value]; + const parent = path.parent as NamedNode | null; + if ( + (parent?.type === SyntaxType.UnaryExpression && !node.comments) || + (parent?.type === SyntaxType.ArrayAccess && parent.arrayNode === node) || + ((parent?.type === SyntaxType.ExplicitConstructorInvocation || + parent?.type === SyntaxType.FieldAccess || + parent?.type === SyntaxType.MethodInvocation) && + parent.objectNode === node) || + parent?.type === SyntaxType.MethodReference || + parent?.type === SyntaxType.ObjectCreationExpression + ) { + return group([indent([softline, ...parts]), softline]); + } + + return parts; }, object_creation_expression(path, print) { @@ -594,7 +684,7 @@ export default { array_access: printMemberChain, method_reference(path, print) { - return path.map(print, "children"); + return group(path.map(print, "children")); }, template_expression(path, print) { @@ -628,20 +718,14 @@ export default { }, guard(path, print) { - const hasParentheses = - path.node.namedChildren[0].type === SyntaxType.ParenthesizedExpression; - const expression = path.call(print, "namedChildren", 0); - return [ "when ", - hasParentheses - ? expression - : group([ - ifBreak("("), - indent([softline, expression]), - softline, - ifBreak(")") - ]) + group([ + ifBreak("("), + indent([softline, path.call(print, "namedChildren", 0)]), + softline, + ifBreak(")") + ]) ]; } } satisfies Partial; @@ -737,6 +821,36 @@ function printArrowFunctionSignatures( return group(indent(join([" ->", line], signatureDocs))); } +function printArrowFunctionBody( + path: NamedNodePath, + args: unknown, + { + bodyDoc, + bodyComments, + shouldPutBodyOnSameLine + }: { + bodyDoc: Doc; + bodyComments: Doc[]; + shouldPutBodyOnSameLine: boolean; + } +) { + // if the arrow function is expanded as last argument, we are adding a + // level of indentation and need to add a softline to align the closing ) + // with the opening (. + const trailingSpace = + args && + typeof args === "object" && + "expandLastArg" in args && + args.expandLastArg && + !path.node.comments + ? softline + : ""; + + return shouldPutBodyOnSameLine + ? [" ", bodyDoc, bodyComments] + : [indent([line, bodyDoc, bodyComments]), trailingSpace]; +} + function shouldPrintParamsWithoutParens( path: NamedNodePath, options: JavaParserOptions @@ -1242,84 +1356,10 @@ function printBinaryExpressions( parts.push(right); - return parent?.type === SyntaxType.BinaryExpression && - needsParentheses(parent.operatorNode.type, operator) - ? ["(", ...parts, ")"] - : parts; + return parts; } const logicalOperators = new Set(["||", "&&"]); -const equalityOperators = new Set(["==", "!="]); -const multiplicativeOperators = new Set(["*", "/", "%"]); -const bitshiftOperators = new Set([">>", ">>>", "<<"]); - -function shouldFlatten(parentOp: string, nodeOp: string) { - if (getPrecedence(nodeOp) !== getPrecedence(parentOp)) { - return false; - } - - // x == y == z --> (x == y) == z - if (equalityOperators.has(parentOp) && equalityOperators.has(nodeOp)) { - return false; - } - - // x * y % z --> (x * y) % z - if ( - (nodeOp === "%" && multiplicativeOperators.has(parentOp)) || - (parentOp === "%" && multiplicativeOperators.has(nodeOp)) - ) { - return false; - } - - // x * y / z --> (x * y) / z - // x / y * z --> (x / y) * z - if ( - nodeOp !== parentOp && - multiplicativeOperators.has(nodeOp) && - multiplicativeOperators.has(parentOp) - ) { - return false; - } - - // x << y << z --> (x << y) << z - if (bitshiftOperators.has(parentOp) && bitshiftOperators.has(nodeOp)) { - return false; - } - - return true; -} - -const PRECEDENCE = new Map( - [ - ["||"], - ["&&"], - ["|"], - ["^"], - ["&"], - ["==", "!="], - ["<", ">", "<=", ">=", "instanceof"], - ["<<", ">>", ">>>"], - ["+", "-"], - ["*", "/", "%"] - ].flatMap((operators, index) => operators.map(operator => [operator, index])) -); -function getPrecedence(operator: string) { - return PRECEDENCE.get(operator) ?? -1; -} - -function needsParentheses(parentOperator: string, operator: string) { - return ( - (operator === "&&" && parentOperator === "||") || - (["|", "^", "&", "<<", ">>", ">>>"].includes(parentOperator) && - getPrecedence(operator) > getPrecedence(parentOperator)) || - [operator, parentOperator].every(o => ["==", "!="].includes(o)) || - [operator, parentOperator].every(o => ["<<", ">>", ">>>"].includes(o)) || - (operator === "*" && parentOperator === "/") || - (operator === "/" && parentOperator === "*") || - (operator === "%" && ["+", "-", "*", "/"].includes(parentOperator)) || - (["*", "/"].includes(operator) && parentOperator === "%") - ); -} function isSimpleCallArgument(node: NamedNode, depth = 2): boolean { if (depth <= 0) { @@ -1386,8 +1426,7 @@ function isLiteral(node: NamedNode) { SyntaxType.HexIntegerLiteral, SyntaxType.OctalIntegerLiteral, SyntaxType.CharacterLiteral - ].includes(node.type) || - (node.type === SyntaxType.StringLiteral && node.children[0].value === '"') + ].includes(node.type) || isStringLiteral(node) ); } @@ -1473,10 +1512,6 @@ function shouldExpandFirstArg(args: NamedNode[]) { } function isHopefullyShortCallArgument(node: NamedNode) { - if (node.type === SyntaxType.ParenthesizedExpression) { - return isHopefullyShortCallArgument(node.namedChildren[0]); - } - if ( (node.type === SyntaxType.MethodInvocation || node.type === SyntaxType.ObjectCreationExpression) && @@ -1495,6 +1530,93 @@ function isHopefullyShortCallArgument(node: NamedNode) { return isSimpleCallArgument(node); } +function printTernaryTest( + path: AstPath, + print: PrintFunction +) { + const { node, parent } = path; + + const printed = path.call(print, "conditionNode"); + /** + * a + * ? b + * : multiline + * test + * node + * ^^ align(2) + * ? d + * : e + */ + if (parent?.type === node.type && parent.alternativeNode === node) { + return align(2, printed); + } + return printed; +} + +function getExpressionChild(node: NamedNode) { + switch (node.type) { + case SyntaxType.AssignmentExpression: + return node.rightNode; + case SyntaxType.VariableDeclarator: + return node.valueNode; + case SyntaxType.UnaryExpression: + return node.operandNode; + case SyntaxType.ReturnStatement: + case SyntaxType.ThrowStatement: + case SyntaxType.YieldStatement: + return node.namedChildren[0]; + default: + return null; + } +} + +function shouldExtraIndentForTernaryExpression( + path: NamedNodePath +) { + const { node } = path; + + let parent: NamedNode | undefined; + let child: NamedNode = node; + for (let ancestorCount = 0; !parent; ancestorCount++) { + const node = path.getParentNode(ancestorCount) as NamedNode; + + if ( + (node.type === SyntaxType.ArrayAccess && node.arrayNode === child) || + ((node.type === SyntaxType.ExplicitConstructorInvocation || + node.type === SyntaxType.FieldAccess || + node.type === SyntaxType.MethodInvocation) && + node.objectNode === child) || + node.type === SyntaxType.MethodReference || + node.type === SyntaxType.ObjectCreationExpression + ) { + child = node; + continue; + } + + // Reached chain root + + if (node.type === SyntaxType.CastExpression && node.valueNode === child) { + parent = path.getParentNode(ancestorCount + 1)!; + child = node; + } else { + parent = node; + } + } + + // Do not add indent to direct `TernaryExpression` + if (child === node) { + return false; + } + + return getExpressionChild(parent) === child; +} + +function isStringLiteral(node: NamedNode) { + return ( + node.type === SyntaxType.StringLiteral && node.children[0].value === '"' + ); +} + class ArgExpansionBailout extends Error { name = "ArgExpansionBailout"; } diff --git a/src/printers/helpers.ts b/src/printers/helpers.ts index db682bca6..f726c1e14 100644 --- a/src/printers/helpers.ts +++ b/src/printers/helpers.ts @@ -5,7 +5,8 @@ import { type CommentNode, type NamedNode, type NamedType, - type SyntaxNode + type SyntaxNode, + type TypeString } from "../node-types.ts"; const { group, hardline, ifBreak, indent, join, line, softline } = builders; @@ -394,6 +395,331 @@ export function textBlockContents(node: NamedNode) { .slice(0, -3); } +const PRECEDENCE = new Map( + [ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "!="], + ["<", ">", "<=", ">=", "instanceof"], + ["<<", ">>", ">>>"], + ["+", "-"], + ["*", "/", "%"] + ].flatMap((operators, index) => operators.map(operator => [operator, index])) +); +export function getPrecedence(operator: string) { + return PRECEDENCE.get(operator) ?? -1; +} + +const equalityOperators = new Set(["==", "!="]); +const multiplicativeOperators = new Set(["*", "/", "%"]); +const bitshiftOperators = new Set([">>", ">>>", "<<"]); + +export function isBitwiseOperator(operator: string) { + return ( + bitshiftOperators.has(operator) || + operator === "|" || + operator === "^" || + operator === "&" + ); +} + +export function shouldFlatten(parentOp: string, nodeOp: string) { + if (getPrecedence(nodeOp) !== getPrecedence(parentOp)) { + return false; + } + + // x == y == z --> (x == y) == z + if (equalityOperators.has(parentOp) && equalityOperators.has(nodeOp)) { + return false; + } + + // x * y % z --> (x * y) % z + if ( + (nodeOp === "%" && multiplicativeOperators.has(parentOp)) || + (parentOp === "%" && multiplicativeOperators.has(nodeOp)) + ) { + return false; + } + + // x * y / z --> (x * y) / z + // x / y * z --> (x / y) * z + if ( + nodeOp !== parentOp && + multiplicativeOperators.has(nodeOp) && + multiplicativeOperators.has(parentOp) + ) { + return false; + } + + // x << y << z --> (x << y) << z + if (bitshiftOperators.has(parentOp) && bitshiftOperators.has(nodeOp)) { + return false; + } + + return true; +} + +export function createTypeCheckFunction(typesArray: T[]) { + const types = new Set(typesArray); + + return ( + node: SyntaxNode | undefined | null + ): node is Extract => + node != null && types.has(node.type); +} + +export const isMember = createTypeCheckFunction([ + SyntaxType.ArrayAccess, + SyntaxType.FieldAccess, + SyntaxType.MethodInvocation +]); + +export function needsParentheses(path: NamedNodePath) { + if (path.isRoot) { + return false; + } + + const { node, parent } = path; + + const parentCheckResult = parentNeedsParentheses(path); + if (typeof parentCheckResult === "boolean") { + return parentCheckResult; + } + + switch (node.type) { + case SyntaxType.SwitchExpression: + return ( + isMember(parent) || + parent?.type === SyntaxType.ExplicitConstructorInvocation || + parent?.type === SyntaxType.MethodReference || + parent?.type === SyntaxType.ObjectCreationExpression + ); + + case SyntaxType.UpdateExpression: + if (parent?.type === SyntaxType.UnaryExpression) { + return node.children[0].type.startsWith(parent.operatorNode.type); + } + // else fallthrough + case SyntaxType.UnaryExpression: + switch (parent?.type) { + case SyntaxType.UnaryExpression: + return ( + node.type === SyntaxType.UnaryExpression && + node.operatorNode.type === parent.operatorNode.type && + (node.operatorNode.type === "+" || node.operatorNode.type === "-") + ); + + case SyntaxType.InstanceofExpression: + // A user typing `!foo instanceof Bar` probably intended + // `!(foo instanceof Bar)`, so format to `(!foo) instance Bar` to what is + // really happening + return ( + parent.leftNode === node && node.type === SyntaxType.UnaryExpression + ); + + default: + return false; + } + + case SyntaxType.BinaryExpression: + case SyntaxType.InstanceofExpression: + if (parent?.type === SyntaxType.UpdateExpression) { + return true; + } + + // fallthrough + case SyntaxType.CastExpression: + switch (parent?.type) { + case SyntaxType.CastExpression: + // example: (Baz) (Bar) foo + return node.type !== SyntaxType.CastExpression; + + case SyntaxType.MethodReference: + case SyntaxType.ObjectCreationExpression: + case SyntaxType.UpdateExpression: + return true; + case SyntaxType.UnaryExpression: + // `UnaryExpression` adds parentheses and indention when argument has comment + if (!node.comments) { + return true; + } + break; + + case SyntaxType.ExplicitConstructorInvocation: + case SyntaxType.FieldAccess: + case SyntaxType.MethodInvocation: + return parent.objectNode === node; + + case SyntaxType.ArrayAccess: + return parent.arrayNode === node; + + case SyntaxType.BinaryExpression: + case SyntaxType.InstanceofExpression: { + if (node.type === SyntaxType.CastExpression) { + return false; + } + + if ( + parent.type === SyntaxType.BinaryExpression && + isLogicalOperator(parent.operatorNode) && + node.type === SyntaxType.BinaryExpression && + isLogicalOperator(node.operatorNode) + ) { + return parent.operatorNode.type !== node.operatorNode.type; + } + + const operator = + node.type === SyntaxType.InstanceofExpression + ? "instanceof" + : node.operatorNode.type; + const precedence = getPrecedence(operator); + const parentOperator = + parent.type === SyntaxType.InstanceofExpression + ? "instanceof" + : parent.operatorNode.type; + const parentPrecedence = getPrecedence(parentOperator); + + if (parentPrecedence > precedence) { + return true; + } + + if (parent.rightNode === node && parentPrecedence === precedence) { + return true; + } + + if ( + parentPrecedence === precedence && + !shouldFlatten(parentOperator, operator) + ) { + return true; + } + + if ( + parentPrecedence < precedence && + operator === "%" && + (parentOperator === "+" || parentOperator === "-") + ) { + return true; + } + + // Add parenthesis when working with bitwise operators + // It's not strictly needed but helps with code understanding + if (isBitwiseOperator(parentOperator)) { + return true; + } + + return false; + } + + default: + return false; + } + break; + + case SyntaxType.AssignmentExpression: + if ( + parent?.type === SyntaxType.ForStatement && + (parent.initNodes.includes(node) || parent.updateNodes.includes(node)) + ) { + return false; + } + + if ( + parent?.type === SyntaxType.ExpressionStatement && + parent.namedChildren[0] === node + ) { + return false; + } + + if (parent?.type === SyntaxType.AssignmentExpression) { + return false; + } + + return true; + + case SyntaxType.TernaryExpression: + switch (parent?.type) { + case SyntaxType.UnaryExpression: + case SyntaxType.BinaryExpression: + case SyntaxType.CastExpression: + case SyntaxType.MethodReference: + case SyntaxType.ObjectCreationExpression: + return true; + + case SyntaxType.TernaryExpression: + return parent.conditionNode === node; + + case SyntaxType.ExplicitConstructorInvocation: + case SyntaxType.FieldAccess: + case SyntaxType.MethodInvocation: + return parent.objectNode === node; + + case SyntaxType.ArrayAccess: + return parent.arrayNode === node; + + default: + return false; + } + } + + return false; +} + +export function returnArgumentHasLeadingComment(node: NamedNode) { + return node.comments?.some( + comment => + comment.leading && + (comment.type === SyntaxType.LineComment || comment.start < comment.end) + ); +} + +export const isReturnOrThrowStatement = createTypeCheckFunction([ + SyntaxType.ReturnStatement, + SyntaxType.ThrowStatement +]); + +function parentNeedsParentheses(path: AstPath) { + const { parent } = path; + + switch (parent?.type) { + case SyntaxType.ReturnStatement: + case SyntaxType.ThrowStatement: + if (willReturnOrThrowStatementBreak(path)) { + return false; + } + break; + } +} + +function willReturnOrThrowStatementBreak(path: NamedNodePath) { + const { parent } = path; + if (!isReturnOrThrowStatement(parent)) { + return false; + } + + /* + When `ReturnStatement` or `ThrowStatement` breaks, parentheses will be added around it's argument. + So don't need add parentheses again. + But we can't know how the argument printed, so only matches cases that will break for sure + */ + + const { node } = path; + + if ( + node.type === SyntaxType.AssignmentExpression && + returnArgumentHasLeadingComment(node) + ) { + return true; + } + + return false; +} + +const isLogicalOperator = createTypeCheckFunction(["||", "&&"]); + function isSimpleType(node: NamedNode): boolean { const { type, children, namedChildren } = node; const lastNamedChild = namedChildren.at(-1); diff --git a/test/unit-test/assert/_output.java b/test/unit-test/assert/_output.java index 6c4d65594..1c0e52b5d 100644 --- a/test/unit-test/assert/_output.java +++ b/test/unit-test/assert/_output.java @@ -1,10 +1,10 @@ public class Assert { public void assertBooleanExpression(String myVar) { - assert (myVar != null); + assert myVar != null; } public void assertValueExpression(String myVar) { - assert (myVar != null) : "text"; + assert myVar != null : "text"; } } diff --git a/test/unit-test/binary_expressions/operator-position-end/_output.java b/test/unit-test/binary_expressions/operator-position-end/_output.java index 47140de83..90edb840e 100644 --- a/test/unit-test/binary_expressions/operator-position-end/_output.java +++ b/test/unit-test/binary_expressions/operator-position-end/_output.java @@ -1,8 +1,8 @@ public class BinaryOperations { public void binaryOperation() { - int alpha = (left) << right; - boolean beta = (left) < right; + int alpha = left << right; + boolean beta = left < right; } @Annotation( diff --git a/test/unit-test/binary_expressions/operator-position-start/_output.java b/test/unit-test/binary_expressions/operator-position-start/_output.java index e97cb9b92..a3f99087b 100644 --- a/test/unit-test/binary_expressions/operator-position-start/_output.java +++ b/test/unit-test/binary_expressions/operator-position-start/_output.java @@ -1,8 +1,8 @@ public class BinaryOperations { public void binaryOperation() { - int alpha = (left) << right; - boolean beta = (left) < right; + int alpha = left << right; + boolean beta = left < right; } @Annotation( diff --git a/test/unit-test/cast/_output.java b/test/unit-test/cast/_output.java index c9d3bb80a..00ea668b4 100644 --- a/test/unit-test/cast/_output.java +++ b/test/unit-test/cast/_output.java @@ -13,16 +13,16 @@ void should_cast_with_additional_bounds() { foo((A & B & C) obj); foo( ( - Aaeaozeaonzeoazneaozenazone - & Bazoieoainzeonaozenoazne - & Cjneazeanezoanezoanzeoaneonazeono + Aaeaozeaonzeoazneaozenazone & + Bazoieoainzeonaozenoazne & + Cjneazeanezoanezoanzeoaneonazeono ) obj ); foo( ( - Aaeaozeaonzeoazneaozenazone - & Bazoieoainzeonaozenoazne - & Cjneazeanezoanezoanzeoaneonazeono + Aaeaozeaonzeoazneaozenazone & + Bazoieoainzeonaozenoazne & + Cjneazeanezoanezoanzeoaneonazeono ) (othrElement, value) -> othrElement + value ); } diff --git a/test/unit-test/comments/class/_output.java b/test/unit-test/comments/class/_output.java index 4fab15e77..e4029b9d1 100644 --- a/test/unit-test/comments/class/_output.java +++ b/test/unit-test/comments/class/_output.java @@ -134,7 +134,7 @@ public static ArrayTable create( * @throws NullPointerException if {@code table} has a null key */ public static ArrayTable create(Table table) { - return (table instanceof ArrayTable) + return table instanceof ArrayTable ? new ArrayTable((ArrayTable) table) : new ArrayTable(table); } @@ -454,7 +454,7 @@ public boolean containsValue(@Nullable Object value) { public V get(@Nullable Object rowKey, @Nullable Object columnKey) { Integer rowIndex = rowKeyToIndex.get(rowKey); Integer columnIndex = columnKeyToIndex.get(columnKey); - return (rowIndex == null || columnIndex == null) + return rowIndex == null || columnIndex == null ? null : at(rowIndex, columnIndex); } @@ -631,7 +631,7 @@ private V getValue(int index) { public Map column(C columnKey) { checkNotNull(columnKey); Integer columnIndex = columnKeyToIndex.get(columnKey); - return (columnIndex == null) + return columnIndex == null ? ImmutableMap.of() : new Column(columnIndex); } @@ -677,7 +677,7 @@ public ImmutableSet columnKeySet() { @Override public Map> columnMap() { ColumnMap map = columnMap; - return (map == null) ? columnMap = new ColumnMap() : map; + return map == null ? (columnMap = new ColumnMap()) : map; } @WeakOuter @@ -723,7 +723,7 @@ public Map put(C key, Map value) { public Map row(R rowKey) { checkNotNull(rowKey); Integer rowIndex = rowKeyToIndex.get(rowKey); - return (rowIndex == null) ? ImmutableMap.of() : new Row(rowIndex); + return rowIndex == null ? ImmutableMap.of() : new Row(rowIndex); } private class Row extends ArrayMap { @@ -767,7 +767,7 @@ public ImmutableSet rowKeySet() { @Override public Map> rowMap() { RowMap map = rowMap; - return (map == null) ? rowMap = new RowMap() : map; + return map == null ? (rowMap = new RowMap()) : map; } @WeakOuter diff --git a/test/unit-test/comments/expression/_output.java b/test/unit-test/comments/expression/_output.java index f4f758c7d..a6141d8bd 100644 --- a/test/unit-test/comments/expression/_output.java +++ b/test/unit-test/comments/expression/_output.java @@ -6,6 +6,6 @@ void example() { a + // comment - (b); + b; } } diff --git a/test/unit-test/conditional-expression/spaces/_output.java b/test/unit-test/conditional-expression/spaces/_output.java index cb404d715..ccd9e3aea 100644 --- a/test/unit-test/conditional-expression/spaces/_output.java +++ b/test/unit-test/conditional-expression/spaces/_output.java @@ -75,10 +75,10 @@ void nestedTernary() { aaaaaaaaaa ? bbbbbbbbbb : cccccccccc - ? dddddddddd - : eeeeeeeeee - ? ffffffffff - : gggggggggg; + ? dddddddddd + : eeeeeeeeee + ? ffffffffff + : gggggggggg; } void ternaryWithComments() { @@ -102,9 +102,9 @@ void ternaryWithComments() { } void ternaryInParentheses() { - (aaaaaaaaaa + aaaaaaaaaa ? bbbbbbbbbb - : cccccccccc.dddddddddd().eeeeeeeeee().ffffffffff()); + : cccccccccc.dddddddddd().eeeeeeeeee().ffffffffff(); } void assignment() { diff --git a/test/unit-test/conditional-expression/tabs/_output.java b/test/unit-test/conditional-expression/tabs/_output.java index 05dafa5aa..a83afe517 100644 --- a/test/unit-test/conditional-expression/tabs/_output.java +++ b/test/unit-test/conditional-expression/tabs/_output.java @@ -101,9 +101,7 @@ void ternaryWithComments() { } void ternaryInParentheses() { - (aaaaaaaaaa - ? bbbbbbbbbb - : cccccccccc.dddddddddd().eeeeeeeeee().ffffffffff()); + aaaaaaaaaa ? bbbbbbbbbb : cccccccccc.dddddddddd().eeeeeeeeee().ffffffffff(); } void assignment() { diff --git a/test/unit-test/expressions/_input.java b/test/unit-test/expressions/_input.java index bf7572e8f..2fe5015aa 100644 --- a/test/unit-test/expressions/_input.java +++ b/test/unit-test/expressions/_input.java @@ -233,20 +233,6 @@ void parenthesesWithTrailingBreak() { (aaaaaaaaaa && bbbbbbbbbb && cccccccccc ? dddddddddd : eeeeeeeeee)[ffffffffff]; } - void parenthesesWithoutBreak() { - (aaaaaaaaaa -> bbbbbbbbbb && cccccccccc ? dddddddddd : eeeeeeeeee).ffffffffff(); - (aaaaaaaaaa -> bbbbbbbbbb && cccccccccc ? dddddddddd : eeeeeeeeee)::ffffffffff; - (aaaaaaaaaa -> bbbbbbbbbb && cccccccccc ? dddddddddd : eeeeeeeeee)[ffffffffff]; - - aaaaaaaaaa = (bbbbbbbbbb -> cccccccccc ? dddddddddd : eeeeeeeeee).ffffffffff(); - aaaaaaaaaa = (bbbbbbbbbb -> cccccccccc ? dddddddddd : eeeeeeeeee)::ffffffffff; - aaaaaaaaaa = (bbbbbbbbbb -> cccccccccc ? dddddddddd : eeeeeeeeee)[ffffffffff]; - - Aaaaaaaaaa aaaaaaaaaa = (bbbbbbbbbb -> cccccccccc ? dddddddddd : eeeeeeeeee).ffffffffff(); - Aaaaaaaaaa aaaaaaaaaa = (bbbbbbbbbb -> cccccccccc ? dddddddddd : eeeeeeeeee)::ffffffffff; - Aaaaaaaaaa aaaaaaaaaa = (bbbbbbbbbb -> cccccccccc ? dddddddddd : eeeeeeeeee)[ffffffffff]; - } - void unaryExpression() { int a = +x; int b = -x; diff --git a/test/unit-test/expressions/_output.java b/test/unit-test/expressions/_output.java index cac90c163..addf3571e 100644 --- a/test/unit-test/expressions/_output.java +++ b/test/unit-test/expressions/_output.java @@ -316,31 +316,6 @@ void parenthesesWithTrailingBreak() { ]; } - void parenthesesWithoutBreak() { - (aaaaaaaaaa -> - bbbbbbbbbb && cccccccccc ? dddddddddd : eeeeeeeeee).ffffffffff(); - (aaaaaaaaaa -> - bbbbbbbbbb && cccccccccc ? dddddddddd : eeeeeeeeee)::ffffffffff; - (aaaaaaaaaa -> bbbbbbbbbb && cccccccccc ? dddddddddd : eeeeeeeeee)[ - ffffffffff - ]; - - aaaaaaaaaa = (bbbbbbbbbb -> - cccccccccc ? dddddddddd : eeeeeeeeee).ffffffffff(); - aaaaaaaaaa = (bbbbbbbbbb -> - cccccccccc ? dddddddddd : eeeeeeeeee)::ffffffffff; - aaaaaaaaaa = (bbbbbbbbbb -> cccccccccc ? dddddddddd : eeeeeeeeee)[ - ffffffffff - ]; - - Aaaaaaaaaa aaaaaaaaaa = (bbbbbbbbbb -> - cccccccccc ? dddddddddd : eeeeeeeeee).ffffffffff(); - Aaaaaaaaaa aaaaaaaaaa = (bbbbbbbbbb -> - cccccccccc ? dddddddddd : eeeeeeeeee)::ffffffffff; - Aaaaaaaaaa aaaaaaaaaa = (bbbbbbbbbb -> - cccccccccc ? dddddddddd : eeeeeeeeee)[ffffffffff]; - } - void unaryExpression() { int a = +x; int b = -x; diff --git a/test/unit-test/lambda/arrow-parens-always/_output.java b/test/unit-test/lambda/arrow-parens-always/_output.java index 81500b35b..fca161042 100644 --- a/test/unit-test/lambda/arrow-parens-always/_output.java +++ b/test/unit-test/lambda/arrow-parens-always/_output.java @@ -568,8 +568,8 @@ void lambdaWithTrailingComments() { } void lambdaInParentheses() { - ((aaaaaaaaaa) -> - bbbbbbbbbb.cccccccccc().dddddddddd().eeeeeeeeee().ffffffffff()); + (aaaaaaaaaa) -> + bbbbbbbbbb.cccccccccc().dddddddddd().eeeeeeeeee().ffffffffff(); } } diff --git a/test/unit-test/lambda/arrow-parens-avoid/_output.java b/test/unit-test/lambda/arrow-parens-avoid/_output.java index 40da78425..cfe2af09d 100644 --- a/test/unit-test/lambda/arrow-parens-avoid/_output.java +++ b/test/unit-test/lambda/arrow-parens-avoid/_output.java @@ -568,8 +568,8 @@ void lambdaWithTrailingComments() { } void lambdaInParentheses() { - (aaaaaaaaaa -> - bbbbbbbbbb.cccccccccc().dddddddddd().eeeeeeeeee().ffffffffff()); + aaaaaaaaaa -> + bbbbbbbbbb.cccccccccc().dddddddddd().eeeeeeeeee().ffffffffff(); } } diff --git a/test/unit-test/yield-statement/_output.java b/test/unit-test/yield-statement/_output.java index db4edb90c..1aea9ba16 100644 --- a/test/unit-test/yield-statement/_output.java +++ b/test/unit-test/yield-statement/_output.java @@ -35,6 +35,6 @@ public int calculate(Day d) { void should_not_throw_on_yield_static_imports() { Thread.yield(); yield(); - yield (a); + yield a; } }