fantomas icon indicating copy to clipboard operation
fantomas copied to clipboard

Broken comma in match expression in parameter invocation

Open piaste opened this issue 3 years ago • 2 comments

Issue created from fantomas-online

Code

module Utils

type U = A of int | B of string

type C() = 
    member this.M (a : int, b : string) = ()

let f () = 
    let u = A 0
    do C().M(
        match u with
             | A i -> i
             | B _ -> 0
             ,
        match u with
             | A _ -> ""
             | B s -> s
    )

Result

module Utils

type U =
    | A of int
    | B of string

type C() =
    member this.M(a: int, b: string) = ()

let f () =
    let u = A 0

    do
        C()
            .M(
                match u with
                | A i -> i
                | B _ -> 0,
                match u with
                | A _ -> ""
                | B s -> s
            )

Problem description

When using a match expression as a parameter argument, the comma that separates parameters can be misinterpreted by the F# compiler as a tuple separator.

Options for disambiguating include:

  • Wrapping the match expression in parenthesis
  • Adding a newline after the match expression (as shown in the pre-format code)

Extra information

  • [x] The formatted result breaks by code.
  • [ ] The formatted result gives compiler warnings.
  • [x] I or my company would be willing to help fix this.

Options

Fantomas Master at 08/11/2021 17:06:14 - 383b729f46ebc01dde282967c68337d5ad55fa17

Default Fantomas configuration

Did you know that you can ignore files when formatting from fantomas-tool or the FAKE targets by using a .fantomasignore file?

piaste avatar Aug 16 '21 09:08 piaste

This is quite the horrible code you have there. I would advise against passing match expressions as arguments. That being said I would accept a PR that extends the check we have in

https://github.com/fsprojects/fantomas/blob/a80f888cbb71e8d0bd65d05c6ceccf9502d30f69/src/Fantomas/CodePrinter.fs#L916-L950

so that in this case, the comma is also flipped.

nojaf avatar Aug 21 '21 12:08 nojaf

I think I also hit this issue but with function matching.

Old Code

module Util

open Aether

let inline non<'a when 'a : equality> (def : 'a) : Lens<'a option, 'a> =
  ( function
      | Some(a) -> a
      | None -> def
  , fun a _ -> if a = def then None else Some(a)
  )

Note that Aether's lens is a tuple of getter and setter so are defining a tuple of functions.

Result of formatting

let inline non<'a when 'a: equality> (def: 'a) : Lens<'a option, 'a> =
  (function
  | Some (a) -> a
  | None -> def, (fun a _ -> if a = def then None else Some(a)))

With the formatting, the F# compiler complains error FS0002: This function takes too many arguments, or is used in a context where a function is not expected

I worked around it by putting parens around the getter function, so fantomas formats it to

let inline non<'a when 'a: equality> (def: 'a) : Lens<'a option, 'a> =
  ((function
   | Some (a) -> a
   | None -> def),
   (fun a _ -> if a = def then None else Some(a)))

Which compiles but is still somewhat confusing to read since you don't instantly notice the comma

wuzzeb avatar Nov 22 '21 19:11 wuzzeb

I believe this issue is fixed. A regression test could close this issue.

nojaf avatar Sep 19 '22 06:09 nojaf