fsprojects / fantomas

FSharp source code formatter
https://fsprojects.github.io/fantomas
Other
772 stars 194 forks source link

Type abbreviation is wrongly formatted #2012

Open nojaf opened 2 years ago

nojaf commented 2 years ago

Issue created from fantomas-online

Code

  type GetParseResultsForFile = string<LocalPath> -> FSharp.Compiler.Text.Position -> Async<ResultOrString<ParseAndCheckResults * string * FSharp.Compiler.Text.ISourceText>>

Result

type GetParseResultsForFile =
    string<LocalPath>
        -> FSharp.Compiler.Text.Position
        -> Async<ResultOrString<ParseAndCheckResults * string * FSharp.Compiler.Text.ISourceText>>

Problem description

Given the recent guidance of the style guide, I would expect that the result should be:

type GetParseResultsForFile =
    string<LocalPath> ->
    FSharp.Compiler.Text.Position ->
        Async<ResultOrString<ParseAndCheckResults * string * FSharp.Compiler.Text.ISourceText>>

However, this leads to invalid code: example

@dsyme any idea why this is happening?

Extra information

Options

Fantomas 4.6 branch at 1/1/1990

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?

dsyme commented 2 years ago

Hmmm I see. We only tested val declarations, e.g.

val F:
    A ->
    B ->
      C

and not type abbreviations:

type X =
    A ->
    B ->
      C

It seems value signatures can use end-of-line -> without indentation of the next line, but types themselves can't

(Note, the thing on the right of a val or member declaration is a value signature, parsed with different parsing rules to a type)

I'm having trouble determining exactly which types this applies to.

  1. The restriction does apply to types inside value signatures, e.g. this will produce an error
// Gives an error
val GetParseResultsForFile :
    (A ->
     B ->
      C)
  1. The restriction does not apply to outermost types in record declarations
// Does not give an error
type R =
    { F: A ->
         B ->
            C }

but does apply to inner types:

// Gives an error
type R =
    { F: (A ->
          B ->
            C) }
  1. The restriction does not apply to the right hand side of function types, e.g. only the outer most position of type abbreviations or parenthesized types:
/// Does not give an error
type X =
    A ->
      B ->
      C ->
      D

/// Does not give an error
val F :
   (A ->
      B ->
      C ->
      D)

I think the safest assumption is to make indent happen for the B part of a multi-line "A -> B" when

A. the type occurs on the right of a type abbreviation, OR B. the type occurs in parentheses

However I guess we should systematically test all other locations where types can occur in the grammar. I'm not actually sure of the problem, I guess it must be a missing rule in the grammar to deal with the offside thing inserted after the ->, but one that is only kicking in for types in very specific locations.