on master help sign in
Fix an idempotence issue with operators chains
commit8eac6bb7a06d968aaeb4fdd5a6c6da293f575f04 authorMark Karpov <markkarpov92@gmail.com> dateFri 15 May 2020, 14:29 (6 years ago) parent1fb65a2
19 files changed+98−80
CHANGELOG.md+3 data/examples/declaration/value/function/operators-out.hs → data/examples/declaration/value/function/operators-0-out.hs data/examples/declaration/value/function/operators.hs → data/examples/declaration/value/function/operators-0.hs data/examples/declaration/value/function/operators-1-out.hs+4 data/examples/declaration/value/function/operators-1.hs+3 data/examples/declaration/value/function/operators-2-out.hs+4 data/examples/declaration/value/function/operators-2.hs+4 data/examples/declaration/value/function/operators-3-out.hs+3 data/examples/declaration/value/function/operators-3.hs+3 data/examples/declaration/value/function/operators-4-out.hs+3 data/examples/declaration/value/function/operators-4.hs+3 data/examples/declaration/value/function/operators-5-out.hs+5 data/examples/declaration/value/function/operators-5.hs+4 data/examples/declaration/value/function/operators-6-out.hs+9 data/examples/declaration/value/function/operators-6.hs+7 default.nix−1 expected-failures/idris.txt−18 expected-failures/stack.txt−5 src/Ormolu/Printer/Operators.hs+43−56
CHANGELOG.md+3
@@ -35,6 +35,9 @@
3535* Fixed non-idempotent transformation of partly documented data definition.
3636 [Issue 590](https://github.com/tweag/ormolu/issues/590).
3737
38+* Fixed an idempotence issue related to operators. [Issue
39+ 522](https://github.com/tweag/ormolu/issues/522).
40+
3841* Renamed the `--check-idempotency` flag to `--check-idempotence`.
3942 Apparently only the latter is correct.
4043
data/examples/declaration/value/function/operators-out.hs → data/examples/declaration/value/function/operators-0-out.hsrenamed

The content is the same.

data/examples/declaration/value/function/operators.hs → data/examples/declaration/value/function/operators-0.hsrenamed

The content is the same.

data/examples/declaration/value/function/operators-1-out.hsnew file+4
@@ -0,0 +1,4 @@
1+foo =
2+ f
3+ . g
4+ =<< h . i
data/examples/declaration/value/function/operators-1.hsnew file+3
@@ -0,0 +1,3 @@
1+foo = f
2+ . g
3+ =<< h . i
data/examples/declaration/value/function/operators-2-out.hsnew file+4
@@ -0,0 +1,4 @@
1+foo n
2+ | x || y && z || n ** x
3+ || x && n =
4+ 42
data/examples/declaration/value/function/operators-2.hsnew file+4
@@ -0,0 +1,4 @@
1+foo n
2+ | x || y && z || n ** x
3+ || x && n =
4+ 42
data/examples/declaration/value/function/operators-3-out.hsnew file+3
@@ -0,0 +1,3 @@
1+foo =
2+ op <> n <+> colon <+> prettySe <+> text "="
3+ <+> prettySe <> text sc
data/examples/declaration/value/function/operators-3.hsnew file+3
@@ -0,0 +1,3 @@
1+foo =
2+ op <> n <+> colon <+> prettySe <+> text "=" <+>
3+ prettySe <> text sc
data/examples/declaration/value/function/operators-4-out.hsnew file+3
@@ -0,0 +1,3 @@
1+foo =
2+ line <> bindingOf <+> text "=" <+> tPretty <+> colon
3+ <+> align <> prettyPs
data/examples/declaration/value/function/operators-4.hsnew file+3
@@ -0,0 +1,3 @@
1+foo =
2+ line <> bindingOf <+> text "=" <+> tPretty <+> colon <+>
3+ align <> prettyPs
data/examples/declaration/value/function/operators-5-out.hsnew file+5
@@ -0,0 +1,5 @@
1+foo =
2+ map bar $
3+ [ baz
4+ ]
5+ ++ quux
data/examples/declaration/value/function/operators-5.hsnew file+4
@@ -0,0 +1,4 @@
1+foo =
2+ map bar $
3+ [ baz
4+ ] ++ quux
data/examples/declaration/value/function/operators-6-out.hsnew file+9
@@ -0,0 +1,9 @@
1+type PermuteRef =
2+ "a"
3+ :> ( "b" :> "c" :> End
4+ :<|> "c" :> "b" :> End
5+ )
6+ :<|> "b"
7+ :> ( "a" :> "c" :> End
8+ :<|> "c" :> "a" :> End
9+ )
data/examples/declaration/value/function/operators-6.hsnew file+7
@@ -0,0 +1,7 @@
1+type PermuteRef =
2+ "a" :> ( "b" :> "c" :> End
3+ :<|> "c" :> "b" :> End
4+ )
5+ :<|> "b" :> ( "a" :> "c" :> End
6+ :<|> "c" :> "a" :> End
7+ )
default.nix−1
@@ -40,7 +40,6 @@ let
4040 "pandoc"
4141 "pipes"
4242 "purescript"
43− "stack"
4443 ];
4544 ormolizedPackages = doCheck:
4645 pkgs.lib.mapAttrs (name: p: ormolize {
expected-failures/idris.txt−18
@@ -1,15 +1,3 @@
1−Formatting is not idempotent:
2− src/Idris/AbsSyntaxTree.hs<rendered>:2105:48
3− before: "t_impl ppo && uname "
4− after: "t_impl ppo\n "
5−Please, consider reporting the bug.
6−
7−Formatting is not idempotent:
8− src/Idris/Core/TT.hs<rendered>:1963:13
9− before: " text op <> pretty"
10− after: " text op\n <"
11−Please, consider reporting the bug.
12−
131The GHC parser (in Haddock mode) failed:
142 src/Idris/Parser.hs:1052:1
153 parse error on input `@'
@@ -21,9 +9,3 @@ Please, consider reporting the bug.
219The GHC parser (in Haddock mode) failed:
2210 src/Idris/Parser/Expr.hs:75:1
2311 parse error on input `@'
24−
25−Formatting is not idempotent:
26− src/Idris/Prover.hs<rendered>:239:10
27− before: " line <> bindin"
28− after: " line\n <"
29−Please, consider reporting the bug.
expected-failures/stack.txtdeleted−5
@@ -1,5 +0,0 @@
1−Formatting is not idempotent:
2− src/Stack/Options/DockerParser.hs<rendered>:36:10
3− before: " <$> opti"
4− after: " <$> option"
5−Please, consider reporting the bug.
src/Ormolu/Printer/Operators.hs+43−56
@@ -12,8 +12,9 @@ where
1212
1313import Data.Function (on)
1414import qualified Data.List as L
15+import Data.Map.Strict (Map)
16+import qualified Data.Map.Strict as M
1517import Data.Maybe (fromMaybe, mapMaybe)
16−import Data.Ord (Down (Down), comparing)
1718import GHC
1819import OccName (mkVarOcc)
1920import Ormolu.Utils (unSrcSpan)
@@ -56,7 +57,7 @@ reassociateOpTree getOpName opTree =
5657reassociateOpTreeWith ::
5758 forall ty op.
5859 -- | Fixity map for operators
59− [(RdrName, Fixity)] ->
60+ Map RdrName Fixity ->
6061 -- | How to get the name of an operator
6162 (op -> Maybe RdrName) ->
6263 -- | Original 'OpTree'
@@ -68,12 +69,12 @@ reassociateOpTreeWith fixityMap getOpName = go
6869 fixityOf :: op -> Fixity
6970 fixityOf op = fromMaybe defaultFixity $ do
7071 opName <- getOpName op
71− lookup opName fixityMap
72+ M.lookup opName fixityMap
7273 -- Here, left branch is already associated and the root alongside with
7374 -- the right branch is right-associated. This function picks up one item
7475 -- from the right and inserts it correctly to the left.
7576 --
76− -- Also, we are using the 'compareFixity' function which returns if the
77+ -- Also, we are using the 'compareFixity' function which tells if the
7778 -- expression should associate to right.
7879 go :: OpTree ty op -> OpTree ty op
7980 -- base cases
@@ -93,6 +94,16 @@ reassociateOpTreeWith fixityMap getOpName = go
9394 then go $ OpBranch (OpBranch l op (go $ OpBranch r op' l')) op'' r'
9495 else go $ OpBranch (OpBranch (OpBranch l op r) op' l') op'' r'
9596
97+-- | A score assigned to an operator.
98+data Score
99+ = -- | The operator was placed at the beginning of a line
100+ AtBeginning Int
101+ | -- | The operator was placed at the end of a line
102+ AtEnd
103+ | -- | The operator was placed in between arguments on a single line
104+ InBetween
105+ deriving (Eq, Ord)
106+
96107-- | Build a map of inferred 'Fixity's from an 'OpTree'.
97108buildFixityMap ::
98109 forall ty op.
@@ -101,23 +112,26 @@ buildFixityMap ::
101112 -- | Operator tree
102113 OpTree (Located ty) (Located op) ->
103114 -- | Fixity map
104− [(RdrName, Fixity)]
115+ Map RdrName Fixity
105116buildFixityMap getOpName opTree =
106− concatMap (\(i, ns) -> map (\(n, _) -> (n, fixity i InfixL)) ns)
107− . zip [0 ..]
108− . L.groupBy (doubleWithinEps 0.00001 `on` snd)
109− . (overrides ++)
110− . modeScores
117+ addOverrides
118+ . M.fromList
119+ . concatMap (\(i, ns) -> map (\(n, _) -> (n, fixity i InfixL)) ns)
120+ . zip [1 ..]
121+ . L.groupBy ((==) `on` snd)
122+ . selectScores
111123 $ score opTree
112124 where
113− -- Add a special case for ($), since it is pretty unlikely for someone
114− -- to override it.
115− overrides :: [(RdrName, Double)]
116− overrides =
117− [ (mkRdrUnqual $ mkVarOcc "$", -1)
118− ]
119− -- Assign scores to operators based on their location in the source.
120− score :: OpTree (Located ty) (Located op) -> [(RdrName, Double)]
125+ addOverrides :: Map RdrName Fixity -> Map RdrName Fixity
126+ addOverrides m =
127+ let mk k v = (mkRdrUnqual (mkVarOcc k), fixity v InfixL)
128+ in M.fromList
129+ [ mk "$" 0,
130+ mk "." 9
131+ ]
132+ `M.union` m
133+ fixity = Fixity NoSourceText
134+ score :: OpTree (Located ty) (Located op) -> [(RdrName, Score)]
121135 score (OpNode _) = []
122136 score (OpBranch l o r) = fromMaybe (score r) $ do
123137 -- If we fail to get any of these, 'defaultFixity' will be used by
@@ -129,46 +143,25 @@ buildFixityMap getOpName opTree =
129143 oc <- srcSpanStartCol <$> unSrcSpan (getLoc o) -- operator column
130144 opName <- getOpName (unLoc o)
131145 let s
132− | le < ob =
133− -- if the operator is in the beginning of a line, assign
134− -- a score relative to its column within range [0, 1).
135− fromIntegral oc / fromIntegral (maxCol + 1)
136− | oe < rb =
137− -- if the operator is in the end of the line, assign the
138− -- score 1.
139− 1
140− | otherwise =
141− 2 -- otherwise, assign a high score.
146+ | le < ob = AtBeginning oc
147+ | oe < rb = AtEnd
148+ | otherwise = InBetween
142149 return $ (opName, s) : score r
143− -- Pick the most common score per 'RdrName'.
144− modeScores :: [(RdrName, Double)] -> [(RdrName, Double)]
145− modeScores =
150+ selectScores :: [(RdrName, Score)] -> [(RdrName, Score)]
151+ selectScores =
146152 L.sortOn snd
147153 . mapMaybe
148154 ( \case
149155 [] -> Nothing
150− xs@((n, _) : _) -> Just (n, mode $ map snd xs)
156+ xs@((n, _) : _) -> Just (n, selectScore $ map snd xs)
151157 )
152158 . L.groupBy ((==) `on` fst)
153159 . L.sort
154− -- Return the most common number, leaning to the smaller
155− -- one in case of a tie.
156− mode :: [Double] -> Double
157− mode =
158− head
159− . L.minimumBy (comparing (Down . length))
160− . L.groupBy (doubleWithinEps 0.0001)
161− . L.sort
162− -- The start column of the rightmost operator.
163− maxCol = go opTree
164− where
165− go (OpNode (L _ _)) = 0
166− go (OpBranch l (L o _) r) =
167− maximum
168− [ go l,
169− maybe 0 srcSpanStartCol (unSrcSpan o),
170− go r
171− ]
160+ selectScore :: [Score] -> Score
161+ selectScore xs =
162+ case filter (/= InBetween) xs of
163+ [] -> InBetween
164+ xs' -> maximum xs'
172165
173166----------------------------------------------------------------------------
174167-- Helpers
@@ -182,9 +175,3 @@ normalizeOpTree (OpBranch (OpNode l) lop r) =
182175 OpBranch (OpNode l) lop (normalizeOpTree r)
183176normalizeOpTree (OpBranch (OpBranch l' lop' r') lop r) =
184177 normalizeOpTree (OpBranch l' lop' (OpBranch r' lop r))
185−
186−fixity :: Int -> FixityDirection -> Fixity
187−fixity = Fixity NoSourceText
188−
189−doubleWithinEps :: Double -> Double -> Double -> Bool
190−doubleWithinEps eps a b = abs (a - b) < eps