tweag / ormolu

A formatter for Haskell source code
https://ormolu-live.tweag.io
Other
958 stars 83 forks source link

"AST of input and AST of formatted code differ" with MultiWayIf: a block-level 'if' applied to an expression #488

Closed ktfleming closed 4 years ago

ktfleming commented 4 years ago

Running ormolu (version 0.0.2.0) on the following code

{-# LANGUAGE MultiWayIf #-}

if | p -> f
   | otherwise -> g
 x

produces the error

AST of input and AST of formatted code differ.
    at ormolu_test.hs:(3,1)-(5,2)
Please, consider reporting the bug.
neongreen commented 4 years ago

Furthermore:

{-# LANGUAGE MultiWayIf #-}

(if | p -> f
    | otherwise -> g)
  x
{-# LANGUAGE MultiWayIf #-}

( if
    | p -> f
    | otherwise -> g
)
  x
Parsing of formatted code failed:
  <input><rendered>:6:1
  parse error (possibly incorrect indentation or mismatched brackets)
Please, consider reporting the bug.
neongreen commented 4 years ago

I don't know if anybody would actually ever do such a thing, but it's not meaningless when interpreted as a top-level splice:

{-# LANGUAGE TemplateHaskell #-}

(if genLenses 
   then makeLenses
   else const (pure []))
 ''X
ktfleming commented 4 years ago

git bisect suggests that this error started appearing with commit ee3c506889ae3396e76a9def6f9a85f40d1c5799.

mrkkrp commented 4 years ago

OK, this is an interesting one. Apparently if we want to keep the results of #307 and increment indentation by 2 (as we should) the only option is to increment indentation for this x argument by exactly 1. If we print x with indentation 0, it's not an argument of the if block. If we bump it by 2, it apparently becomes an argument of g, which also changes the meaning of the program. But if we want to use only increments of 2, then using 1-space indent is no-go. Difficult situation.

mrkkrp commented 4 years ago

@neongreen Note that your example works if it's not a top-level expression:

baz =
  (if | p -> f
      | otherwise -> g)
    x
mrkkrp commented 4 years ago

I imagine one solution here could be: print multi-way if like this:

baz =
  if  | p -> f
      | otherwise -> g
    x
neongreen commented 4 years ago

Looks good to me.