[Repo Assist] Parenthesise lambda LHS of infix operator in single-line form to preserve semantics (#3274)#3276
Closed
github-actions[bot] wants to merge 2 commits intomainfrom
Conversation
When a lambda (or if/then/else) appears as the left-hand side of an
infix operator and the whole expression fits on one line, Fantomas was
emitting it without parentheses:
Before: fun x -> {| X = x |} <*| op
After: (fun x -> {| X = x |}) <*| op
Without parens, the parser sees the lambda as consuming the entire
right-hand side (including the operator and its argument), which is
semantically different from the original multiline code where the
operator applied to the whole lambda.
This is analogous to the existing special-casing for pipe operators
(isNewLineInfixOp), but applies to all infix operators in the
single-line (short) layout.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ns-c790770fc534a889
28 tasks
Contributor
|
Gonna solve this in a larger refactor to have a more overall consistent story. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3274
🤖 This PR was created by Repo Assist, an automated AI assistant. Please review carefully.
Problem
When a lambda appears as the left-hand side of a custom infix operator, and the entire expression fits on one line, Fantomas was emitting it without parentheses — changing the semantics of the code:
Input (4 variations, all with
(fun x -> ...) <*| opsemantics):Before (wrong — changes meaning):
This parses as
fun x -> ({| X = x |} <*| op)— the operator is now inside the lambda body.After (correct):
Root Cause
In
Expr.InfixApphandling,genShortInfixExpr(the single-line layout) callsgenExpr node.LeftHandSidewithout adding parentheses. When the LHS is a lambda,fun x -> body op rhsis re-parsed asfun x -> (body op rhs), which is semantically different.For
newLineInfixOps(|>,>>, etc.), Fantomas already forces multiline layout when the LHS is a lambda. But for other operators (e.g.<*|,<$>,>>=), the short single-line form was used without guarding the semantics.Fix
Add parentheses around the LHS expression in
genShortInfixExprwhen it is a lambda or if/then/else:If the lambda was already explicitly parenthesised in the source, its Oak representation is
Expr.Paren(Expr.Lambda(...)), which does not matchisLambdaOrIfThenElse, so no double parens are added.Test Status
dotnet build fantomas.sln— succeededdotnet test src/Fantomas.Core.Tests/— 2737 passed, 0 failed, 7 skippedlambda on LHS of custom infix operator is parenthesised to preserve semantics, 3274