1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
15import qualified Data.Map as Map
16import qualified Data.Maybe as Maybe
17import Data.Monoid ((<>))
19import qualified AST.Module.Name as ModuleName
20import qualified Data.Bag as Bag
21import qualified Elm.Name as N
22import qualified Reporting.Helpers as H
23import qualified Reporting.Render.Type as RT
31 = Lambda Type Type [Type]
38 | Type ModuleName.Canonical N.Name [Type]
39 | Record (Map.Map N.Name Type) Extension
41 | Tuple Type Type (Maybe Type)
42 | Alias ModuleName.Canonical N.Name [(N.Name, Type)] Type
51iteratedDealias :: Type -> Type
54 Alias _ _ _ realType ->
55 iteratedDealias realType
65type Localizer = Map.Map (ModuleName.Canonical, N.Name) String
68nameToDoc :: Localizer -> ModuleName.Canonical -> N.Name -> H.Doc
69nameToDoc dict home@(ModuleName.Canonical _ moduleName) name =
70 case Map.lookup (home, name) dict of
72 H.nameToDoc moduleName <> "." <> H.nameToDoc name
78toDoc :: Localizer -> RT.Context -> Type -> H.Doc
83 (toDoc dict RT.Func a)
84 (toDoc dict RT.Func b)
85 (map (toDoc dict RT.Func) cs)
105 Type home name args ->
107 (nameToDoc dict home name)
108 (map (toDoc dict RT.App) args)
112 entryToDocs (fieldName, fieldType) =
113 ( H.nameToDoc fieldName, toDoc dict RT.None fieldType )
116 map entryToDocs (Map.toList fields)
118 RT.record fieldDocs $
121 FlexOpen x -> Just (H.nameToDoc x)
122 RigidOpen x -> Just (H.nameToDoc x)
130 (toDoc dict RT.None a)
131 (toDoc dict RT.None b)
132 (map (toDoc dict RT.None) (Maybe.maybeToList maybeC))
134 Alias home name args _ ->
136 (nameToDoc dict home name)
137 (map (toDoc dict RT.App . snd) args)
144toDiffDocs :: Localizer -> Type -> Type -> (H.Doc, H.Doc, [Problem])
146 case diff dict RT.None a b of
150 Different aDoc bDoc problems ->
151 (aDoc, bDoc, Bag.toList problems)
156 | Different a a (Bag.Bag Problem)
159instance Functor Diff where
162 Similar a b -> Similar (func a) (func b)
163 Different a b ps -> Different (func a) (func b) ps
166instance Applicative Diff where
172 Similar aFunc bFunc ->
174 Similar a b -> Similar (aFunc a) (bFunc b)
175 Different a b ps -> Different (aFunc a) (bFunc b) ps
177 Different aFunc bFunc ps ->
179 Similar a b -> Different (aFunc a) (bFunc b) ps
180 Different a b ps2 -> Different (aFunc a) (bFunc b) (Bag.append ps ps2)
188 = FieldMismatch [N.Name] [N.Name]
193 | BadFlexSuper N.Name Type
194 | BadRigidVar N.Name Type
195 | BadRigidSuper N.Name Type
202diff :: Localizer -> RT.Context -> Type -> Type -> Diff H.Doc
203diff dict ctx tipe1 tipe2 =
204 case (tipe1, tipe2) of
205 (FlexVar x, FlexVar y) | x == y -> pure (H.nameToDoc x)
206 (FlexSuper x, FlexSuper y) | x == y -> pure (H.nameToDoc x)
207 (RigidVar x, RigidVar y) | x == y -> pure (H.nameToDoc x)
208 (RigidSuper x, RigidSuper y) | x == y -> pure (H.nameToDoc x)
210 (Infinite, Infinite) -> pure "∞"
211 (Error, Error) -> pure "?"
212 (Unit, Unit) -> pure "()"
214 (Tuple a b maybeC, Tuple x y maybeZ) ->
216 <$> diff dict RT.None a x
217 <*> diff dict RT.None b y
219 case (maybeC, maybeZ) of
220 (Nothing, Nothing) -> pure []
221 (Just c , Nothing) -> Different [H.dullyellow (toDoc dict RT.None c)] [] (Bag.one TupleMismatch)
222 (Nothing, Just z ) -> Different [] [H.dullyellow (toDoc dict RT.None z)] (Bag.one TupleMismatch)
223 (Just c , Just z ) -> (:[]) <$> diff dict RT.None c z
225 (Record fields1 ext1, Record fields2 ext2) -> diffRecord dict fields1 ext1 fields2 ext2
227 (Type home1 name1 args1, Type home2 name2 args2) | home1 == home2 && name1 == name2 ->
228 RT.apply ctx (nameToDoc dict home1 name1) <$>
229 sequenceA (zipWith (diff dict RT.App) args1 args2)
231 (Alias home1 name1 args1 _, Alias home2 name2 args2 _) | home1 == home2 && name1 == name2 ->
232 RT.apply ctx (nameToDoc dict home1 name1) <$>
233 sequenceA (zipWith (diff dict RT.App) (map snd args1) (map snd args2))
235 (Alias _ _ _ alias1, other) -> diff dict ctx alias1 other
236 (other, Alias _ _ _ alias2) -> diff dict ctx other alias2
238 (Lambda a b cs, Lambda x y zs) -> diffLambda dict ctx (a:b:cs) (x:y:zs)
239 (Lambda a b cs, result ) -> diffLambda dict ctx (a:b:cs) [result]
240 (result , Lambda x y zs) -> diffLambda dict ctx [result] (x:y:zs)
242 (FlexVar x, other) -> Similar (H.nameToDoc x) (toDoc dict ctx other)
243 (other, FlexVar x) -> Similar (toDoc dict ctx other) (H.nameToDoc x)
247 doc1 = H.dullyellow (toDoc dict ctx tipe1)
248 doc2 = H.dullyellow (toDoc dict ctx tipe2)
250 Different doc1 doc2 $
252 (RigidVar x, other) -> Bag.one $ BadRigidVar x other
253 (FlexSuper x, other) -> Bag.one $ BadFlexSuper x other
254 (RigidSuper x, other) -> Bag.one $ BadRigidSuper x other
255 (other, RigidVar x) -> Bag.one $ BadRigidVar x other
256 (other, FlexSuper x) -> Bag.one $ BadFlexSuper x other
257 (other, RigidSuper x) -> Bag.one $ BadRigidSuper x other
259 (Type home1 name1 [], Type home2 name2 []) | isIntFloat home1 name1 home2 name2 -> Bag.one IntFloat
264isIntFloat :: ModuleName.Canonical -> N.Name -> ModuleName.Canonical -> N.Name -> Bool
265isIntFloat home1 name1 home2 name2 =
266 home1 == ModuleName.basics
268 home2 == ModuleName.basics
271 (name1 == N.int && name2 == N.float)
273 (name1 == N.float && name2 == N.int)
282-- INVARIANT: notNull types1 && notNull types2
284diffLambda :: Localizer -> RT.Context -> [Type] -> [Type] -> Diff H.Doc
285diffLambda dict ctx types1 types2 =
287 (result1:revArgs1) = reverse types1
288 (result2:revArgs2) = reverse types2
290 numArgs1 = length revArgs1
291 numArgs2 = length revArgs2
293 resultDiff = diff dict RT.Func result1 result2
296 Similar resultDoc1 resultDoc2 ->
297 if numArgs1 == numArgs2 then
298 (\resultDoc revArgsDoc ->
299 let (x:y:zs) = reverse (resultDoc:revArgsDoc) in
303 <*> sequenceA (zipWith (diff dict RT.Func) revArgs1 revArgs2)
305 else if numArgs1 < numArgs2 then
306 diffArgMismatch dict ctx revArgs1 resultDoc1 revArgs2 resultDoc2
309 diffArgMismatch dict ctx revArgs2 resultDoc2 revArgs1 resultDoc1
311 Different resultDoc1 resultDoc2 problems ->
313 (x1:y1:zs1) = reverse (resultDoc1 : map (toDoc dict RT.Func) revArgs1)
314 (x2:y2:zs2) = reverse (resultDoc2 : map (toDoc dict RT.Func) revArgs2)
317 (RT.lambda ctx x1 y1 zs1)
318 (RT.lambda ctx x2 y2 zs2)
319 (Bag.append (Bag.one ReturnMismatch) problems)
323-- INVARIANT: notNull shortRevArgs && notNull longRevArgs
325diffArgMismatch :: Localizer -> RT.Context -> [Type] -> H.Doc -> [Type] -> H.Doc -> Diff H.Doc
326diffArgMismatch dict ctx shortRevArgs shortResult longRevArgs longResult =
327 case toGreedyMatch dict shortRevArgs longRevArgs of
328 Just (GreedyMatch shortRevArgDocs longRevArgDocs) ->
330 (a:b:cs) = reverse (shortResult:shortRevArgDocs)
331 (x:y:zs) = reverse (longResult:longRevArgDocs)
334 (RT.lambda ctx a b cs)
335 (RT.lambda ctx x y zs)
336 (Bag.one (MissingArgs (length longRevArgs - length shortRevArgs)))
339 case toGreedyMatch dict (reverse shortRevArgs) (reverse longRevArgs) of
340 Just (GreedyMatch shortArgDocs longArgDocs) ->
342 (a:b:cs) = shortArgDocs ++ [shortResult]
343 (x:y:zs) = longArgDocs ++ [longResult ]
346 (RT.lambda ctx a b cs)
347 (RT.lambda ctx x y zs)
348 (Bag.one (MissingArgs (length longRevArgs - length shortRevArgs)))
353 H.dullyellow (toDoc dict RT.Func tipe)
355 (a:b:cs) = reverse (shortResult : map toYellowDoc shortRevArgs)
356 (x:y:zs) = reverse (longResult : map toYellowDoc longRevArgs )
359 (RT.lambda ctx a b cs)
360 (RT.lambda ctx x y zs)
361 (Bag.one (MissingArgs (length longRevArgs - length shortRevArgs)))
369 GreedyMatch [H.Doc] [H.Doc]
373-- INVARIANT: length shorterArgs < length longerArgs
375toGreedyMatch :: Localizer -> [Type] -> [Type] -> Maybe GreedyMatch
376toGreedyMatch dict shorterArgs longerArgs =
377 toGreedyMatchHelp dict shorterArgs longerArgs (GreedyMatch [] [])
380toGreedyMatchHelp :: Localizer -> [Type] -> [Type] -> GreedyMatch -> Maybe GreedyMatch
381toGreedyMatchHelp dict shorterArgs longerArgs match@(GreedyMatch shorterDocs longerDocs) =
384 H.dullyellow (toDoc dict RT.Func tipe)
386 case (shorterArgs, longerArgs) of
388 case diff dict RT.Func x y of
390 toGreedyMatchHelp dict xs ys $
391 GreedyMatch (a:shorterDocs) (b:longerDocs)
394 toGreedyMatchHelp dict shorterArgs ys $
395 GreedyMatch shorterDocs (toYellowDoc y : longerDocs)
401 Just (GreedyMatch shorterDocs (map toYellowDoc longerArgs))
411diffRecord :: Localizer -> Map.Map N.Name Type -> Extension -> Map.Map N.Name Type -> Extension -> Diff H.Doc
412diffRecord dict fields1 ext1 fields2 ext2 =
414 only1 = Map.keys (Map.difference fields1 fields2)
415 only2 = Map.keys (Map.difference fields2 fields1)
417 if null only1 && null only2 then
418 case findBadOverlaps dict fields1 fields2 of
420 case diffExt ext1 ext2 of
422 Similar (toBoringRecord fields1 ext1) (toBoringRecord fields2 ext2)
424 Different dExt1 dExt2 problems ->
425 Different (RT.record [] dExt1) (RT.record [] dExt2) problems
427 overlaps@(bad:bads) ->
428 if length overlaps < Map.size fields1 then
430 (RT.recordSnippet (toOverlapDoc _doc1 bad) (map (toOverlapDoc _doc1) bads))
431 (RT.recordSnippet (toOverlapDoc _doc2 bad) (map (toOverlapDoc _doc2) bads))
432 (foldr Bag.append Bag.empty (map _problems overlaps))
435 (toOverlapRecord _doc1 bad bads ext1)
436 (toOverlapRecord _doc2 bad bads ext2)
437 (foldr Bag.append Bag.empty (map _problems overlaps))
441 (toMissingDoc fields1 only1 ext1)
442 (toMissingDoc fields2 only2 ext2)
443 (Bag.one (FieldMismatch only1 only2))
446toOverlapDoc :: (BadOverlap -> H.Doc) -> BadOverlap -> (H.Doc, H.Doc)
447toOverlapDoc getDoc overlap@(BadOverlap field _ _ _) =
448 (H.nameToDoc field, getDoc overlap)
451toOverlapRecord :: (BadOverlap -> H.Doc) -> BadOverlap -> [BadOverlap] -> Extension -> H.Doc
452toOverlapRecord getDoc bad bads ext =
453 let go = toOverlapDoc getDoc in
455 Closed -> RT.record (map go (bad:bads)) Nothing
456 FlexOpen _ -> RT.recordSnippet (go bad) (map go bads)
457 RigidOpen _ -> RT.recordSnippet (go bad) (map go bads)
460toMissingDoc :: Map.Map N.Name t -> [N.Name] -> Extension -> H.Doc
461toMissingDoc allFields uniqueFields ext =
462 case map emphasizeFieldName uniqueFields of
464 toBoringRecord allFields ext
467 if length uniqueFields < Map.size allFields then
468 RT.recordSnippet doc docs
471 Closed -> RT.record (doc:docs) Nothing
472 FlexOpen _ -> RT.recordSnippet doc docs
473 RigidOpen _ -> RT.recordSnippet doc docs
476emphasizeFieldName :: N.Name -> (H.Doc, H.Doc)
477emphasizeFieldName field =
478 ( H.dullyellow (H.nameToDoc field), "..." )
481toBoringRecord :: Map.Map N.Name t -> Extension -> H.Doc
482toBoringRecord fields ext =
484 Closed -> if Map.null fields then "{}" else "{ ... }"
485 FlexOpen _ -> "{ ... }"
486 RigidOpen _ -> "{ ... }"
490-- DIFF RECORD EXTENSION
493diffExt :: Extension -> Extension -> Diff (Maybe H.Doc)
496 normal = Just . H.nameToDoc
497 yellow = Just . H.dullyellow . H.nameToDoc
499 different x y = Different x y Bag.empty
504 Closed -> Similar Nothing Nothing
505 FlexOpen y -> different Nothing (yellow y)
506 RigidOpen y -> different Nothing (yellow y)
510 Closed -> different (yellow x) Nothing
511 FlexOpen y -> Similar (normal x) (normal y)
512 RigidOpen y -> Similar (normal x) (normal y)
516 Closed -> different (yellow x) Nothing
517 FlexOpen y -> Similar (normal x) (normal y)
520 Similar (normal x) (normal y)
522 Different (yellow x) (yellow y) (Bag.one (BadRigidVar x (RigidVar y)))
534 , _problems :: Bag.Bag Problem
538findBadOverlaps :: Localizer -> Map.Map N.Name Type -> Map.Map N.Name Type -> [BadOverlap]
539findBadOverlaps dict fields1 fields2 =
540 findBadOverlapsHelp [] $ Map.toList $
541 Map.intersectionWith (diff dict RT.None) fields1 fields2
544findBadOverlapsHelp :: [BadOverlap] -> [(N.Name, Diff H.Doc)] -> [BadOverlap]
545findBadOverlapsHelp badOverlaps fieldPairs =
550 (_, Similar _ _) : otherFieldPairs ->
551 findBadOverlapsHelp badOverlaps otherFieldPairs
553 (field, Different aDoc bDoc problems) : otherFieldPairs ->
554 findBadOverlapsHelp (BadOverlap field aDoc bDoc problems : badOverlaps) otherFieldPairs
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list