fsprojects / fantomas

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

Unary operators: inconsistent formatting when arg is literal vs variable #3131

Open pleaseletmesearch opened 2 weeks ago

pleaseletmesearch commented 2 weeks ago

Issue created from fantomas-online

(This is @Smaug123 but from the office.)

Code

let x = ~~1
let y = ~~x

Result

let x = ~~ 1
let y = ~~x

Problem description

The formatting is inconsistent between when the argument of a unary operator is a literal vs a variable. I'd prefer us to stick with ~~1 (removing the space, as we do with the variable case).

Extra information

Options

Fantomas main branch at 2024-09-14T12:20:28Z - 06d86ad91974467f0e82f5f0ca7d3c58eef7e859

Default Fantomas configuration

Did you know that you can ignore files when formatting by using a .fantomasignore file? PS: It's unlikely that someone else will solve your specific issue, as it's something that you have a personal stake in.

nojaf commented 2 weeks ago

Hi Patrick, thanks for bringing this up. I see that the style guide does indeed mention not to place a space. I will investigate whether there is a specific reason for this or if it's just a historical practice.

brianrourkeboll commented 2 weeks ago

I will investigate whether there is a specific reason for this or if it's just a historical practice.

The space is needed (or parentheses are needed) if the numeric literal starts with a unary - or +, which is not currently reflected in the AST. (The unary - or + is, of course, reflected in the AST for non-literal expressions, e.g., -x.)

In the parens analyzer, I had to resort to looking at the source text:

https://github.com/dotnet/fsharp/blob/686032982194a6dd916478d3092aa4c98194bcdc/src/Compiler/Service/SynExpr.fs#L578-L603

// Matches if the given expression starts with a symbol, e.g., <@ … @>, $"…", @"…", +1, -1…
let (|StartsWithSymbol|_|) =
    let (|TextStartsWith|) (m: range) =
        let line = getSourceLineStr m.StartLine
        line[m.StartColumn]

    function
    | SynExpr.Quote _
    | SynExpr.InterpolatedString _
    | SynExpr.Const(SynConst.String(synStringKind = SynStringKind.Verbatim), _)
    | SynExpr.Const(SynConst.Byte _, TextStartsWith '+')
    | SynExpr.Const(SynConst.UInt16 _, TextStartsWith '+')
    | SynExpr.Const(SynConst.UInt32 _, TextStartsWith '+')
    | SynExpr.Const(SynConst.UInt64 _, TextStartsWith '+')
    | SynExpr.Const(SynConst.UIntPtr _, TextStartsWith '+')
    | SynExpr.Const(SynConst.SByte _, TextStartsWith('-' | '+'))
    | SynExpr.Const(SynConst.Int16 _, TextStartsWith('-' | '+'))
    | SynExpr.Const(SynConst.Int32 _, TextStartsWith('-' | '+'))
    | SynExpr.Const(SynConst.Int64 _, TextStartsWith('-' | '+'))
    | SynExpr.Const(SynConst.IntPtr _, TextStartsWith('-' | '+'))
    | SynExpr.Const(SynConst.Decimal _, TextStartsWith('-' | '+'))
    | SynExpr.Const(SynConst.Double _, TextStartsWith('-' | '+'))
    | SynExpr.Const(SynConst.Single _, TextStartsWith('-' | '+'))
    | SynExpr.Const(SynConst.Measure(_, TextStartsWith('-' | '+'), _, _), _)
    | SynExpr.Const(SynConst.UserNum(StartsWith('-' | '+'), _), _) -> Some StartsWithSymbol
    | _ -> None