1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
21import qualified Data.Map as Map
22import qualified Data.Maybe as Maybe
23import Data.Monoid ((<>))
24import qualified Data.Name as Name
26import qualified Data.Bag as Bag
27import qualified Elm.ModuleName as ModuleName
28import qualified Reporting.Doc as D
29import qualified Reporting.Render.Type as RT
30import qualified Reporting.Render.Type.Localizer as L
38 = Lambda Type Type [Type]
42 | FlexSuper Super Name.Name
44 | RigidSuper Super Name.Name
45 | Type ModuleName.Canonical Name.Name [Type]
46 | Record (Map.Map Name.Name Type) Extension
48 | Tuple Type Type (Maybe Type)
49 | Alias ModuleName.Canonical Name.Name [(Name.Name, Type)] Type
66iteratedDealias :: Type -> Type
80toDoc :: L.Localizer -> RT.Context -> Type -> D.Doc
81toDoc localizer ctx tipe =
85 (toDoc localizer RT.Func a)
86 (toDoc localizer RT.Func b)
87 (map (toDoc localizer RT.Func) cs)
107 Type home name args ->
109 (L.toDoc localizer home name)
110 (map (toDoc localizer RT.App) args)
113 RT.record (fieldsToDocs localizer fields) (extToDoc ext)
120 (toDoc localizer RT.None a)
121 (toDoc localizer RT.None b)
122 (map (toDoc localizer RT.None) (Maybe.maybeToList maybeC))
124 Alias home name args _ ->
125 aliasToDoc localizer ctx home name args
128aliasToDoc :: L.Localizer -> RT.Context -> ModuleName.Canonical -> Name.Name -> [(Name.Name, Type)] -> D.Doc
129aliasToDoc localizer ctx home name args =
131 (L.toDoc localizer home name)
132 (map (toDoc localizer RT.App . snd) args)
135fieldsToDocs :: L.Localizer -> Map.Map Name.Name Type -> [(D.Doc, D.Doc)]
136fieldsToDocs localizer fields =
137 Map.foldrWithKey (addField localizer) [] fields
140addField :: L.Localizer -> Name.Name -> Type -> [(D.Doc, D.Doc)] -> [(D.Doc, D.Doc)]
141addField localizer fieldName fieldType docs =
143 f = D.fromName fieldName
144 t = toDoc localizer RT.None fieldType
149extToDoc :: Extension -> Maybe D.Doc
153 FlexOpen x -> Just (D.fromName x)
154 RigidOpen x -> Just (D.fromName x)
167 | Different (Bag.Bag Problem)
181 | BadFlexSuper Direction Super Name.Name Type
182 | BadRigidVar Name.Name Type
183 | BadRigidSuper Super Name.Name Type
184 | FieldTypo Name.Name [Name.Name]
185 | FieldsMissing [Name.Name]
188data Direction = Have | Need
191instance Functor Diff where
192 fmap func (Diff a b status) =
193 Diff (func a) (func b) status
196instance Applicative Diff where
200 (<*>) (Diff aFunc bFunc status1) (Diff aArg bArg status2) =
201 Diff (aFunc aArg) (bFunc bArg) (merge status1 status2)
204merge :: Status -> Status -> Status
205merge status1 status2 =
210 Different problems1 ->
215 Different problems2 ->
216 Different (Bag.append problems1 problems2)
223toComparison :: L.Localizer -> Type -> Type -> (D.Doc, D.Doc, [Problem])
224toComparison localizer tipe1 tipe2 =
225 case toDiff localizer RT.None tipe1 tipe2 of
226 Diff doc1 doc2 Similar ->
229 Diff doc1 doc2 (Different problems) ->
230 (doc1, doc2, Bag.toList problems)
233toDiff :: L.Localizer -> RT.Context -> Type -> Type -> Diff D.Doc
234toDiff localizer ctx tipe1 tipe2 =
235 case (tipe1, tipe2) of
236 (Unit , Unit ) -> same localizer ctx tipe1
237 (Error , Error ) -> same localizer ctx tipe1
238 (Infinite, Infinite) -> same localizer ctx tipe1
240 (FlexVar x, FlexVar y) | x == y -> same localizer ctx tipe1
241 (FlexSuper _ x, FlexSuper _ y) | x == y -> same localizer ctx tipe1
242 (RigidVar x, RigidVar y) | x == y -> same localizer ctx tipe1
243 (RigidSuper _ x, RigidSuper _ y) | x == y -> same localizer ctx tipe1
245 (FlexVar _, _ ) -> similar localizer ctx tipe1 tipe2
246 (_ , FlexVar _) -> similar localizer ctx tipe1 tipe2
248 (FlexSuper s _, t ) | isSuper s t -> similar localizer ctx tipe1 tipe2
249 (t , FlexSuper s _) | isSuper s t -> similar localizer ctx tipe1 tipe2
251 (Lambda a b cs, Lambda x y zs) ->
252 if length cs == length zs then
254 <$> toDiff localizer RT.Func a x
255 <*> toDiff localizer RT.Func b y
256 <*> sequenceA (zipWith (toDiff localizer RT.Func) cs zs)
258 diffLambda localizer ctx a b cs x y zs
260 (Tuple a b Nothing, Tuple x y Nothing) ->
262 <$> toDiff localizer RT.None a x
263 <*> toDiff localizer RT.None b y
266 (Tuple a b (Just c), Tuple x y (Just z)) ->
268 <$> toDiff localizer RT.None a x
269 <*> toDiff localizer RT.None b y
270 <*> ((:[]) <$> toDiff localizer RT.None c z)
272 (Record fields1 ext1, Record fields2 ext2) ->
273 diffRecord localizer fields1 ext1 fields2 ext2
275 (Type home1 name1 args1, Type home2 name2 args2) | home1 == home2 && name1 == name2 ->
276 RT.apply ctx (L.toDoc localizer home1 name1)
277 <$> sequenceA (zipWith (toDiff localizer RT.App) args1 args2)
279 (Alias home1 name1 args1 _, Alias home2 name2 args2 _) | home1 == home2 && name1 == name2 ->
280 RT.apply ctx (L.toDoc localizer home1 name1)
281 <$> sequenceA (zipWith (toDiff localizer RT.App) (map snd args1) (map snd args2))
283 -- start trying to find specific problems
285 (Type home1 name1 args1, Type home2 name2 args2) | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
287 (nameClashToDoc ctx localizer home1 name1 args1)
288 (nameClashToDoc ctx localizer home2 name2 args2)
291 (Type home name [t1], t2) | isMaybe home name && isSimilar (toDiff localizer ctx t1 t2) ->
293 (RT.apply ctx (D.dullyellow (L.toDoc localizer home name)) [toDoc localizer RT.App t1])
294 (toDoc localizer ctx t2)
295 (Bag.one AnythingFromMaybe)
297 (t1, Type home name [t2]) | isList home name && isSimilar (toDiff localizer ctx t1 t2) ->
299 (toDoc localizer ctx t1)
300 (RT.apply ctx (D.dullyellow (L.toDoc localizer home name)) [toDoc localizer RT.App t2])
301 (Bag.one AnythingToList)
303 (Alias home1 name1 args1 t1, t2) ->
304 case diffAliasedRecord localizer t1 t2 of
305 Just (Diff _ doc2 status) ->
306 Diff (D.dullyellow (aliasToDoc localizer ctx home1 name1 args1)) doc2 status
310 Type home2 name2 args2 | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
312 (nameClashToDoc ctx localizer home1 name1 (map snd args1))
313 (nameClashToDoc ctx localizer home2 name2 args2)
318 (D.dullyellow (toDoc localizer ctx tipe1))
319 (D.dullyellow (toDoc localizer ctx tipe2))
322 (t1, Alias home2 name2 args2 t2) ->
323 case diffAliasedRecord localizer t1 t2 of
324 Just (Diff doc1 _ status) ->
325 Diff doc1 (D.dullyellow (aliasToDoc localizer ctx home2 name2 args2)) status
329 Type home1 name1 args1 | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
331 (nameClashToDoc ctx localizer home1 name1 args1)
332 (nameClashToDoc ctx localizer home2 name2 (map snd args2))
337 (D.dullyellow (toDoc localizer ctx tipe1))
338 (D.dullyellow (toDoc localizer ctx tipe2))
343 doc1 = D.dullyellow (toDoc localizer ctx tipe1)
344 doc2 = D.dullyellow (toDoc localizer ctx tipe2)
346 different doc1 doc2 $
348 (RigidVar x, other) -> Bag.one $ BadRigidVar x other
349 (FlexSuper s x, other) -> Bag.one $ BadFlexSuper Have s x other
350 (RigidSuper s x, other) -> Bag.one $ BadRigidSuper s x other
351 (other, RigidVar x) -> Bag.one $ BadRigidVar x other
352 (other, FlexSuper s x) -> Bag.one $ BadFlexSuper Need s x other
353 (other, RigidSuper s x) -> Bag.one $ BadRigidSuper s x other
355 (Type home1 name1 [], Type home2 name2 [])
356 | isInt home1 name1 && isFloat home2 name2 -> Bag.one IntFloat
357 | isFloat home1 name1 && isInt home2 name2 -> Bag.one IntFloat
358 | isInt home1 name1 && isString home2 name2 -> Bag.one StringFromInt
359 | isFloat home1 name1 && isString home2 name2 -> Bag.one StringFromFloat
360 | isString home1 name1 && isInt home2 name2 -> Bag.one StringToInt
361 | isString home1 name1 && isFloat home2 name2 -> Bag.one StringToFloat
362 | isBool home2 name2 -> Bag.one AnythingToBool
372same :: L.Localizer -> RT.Context -> Type -> Diff D.Doc
373same localizer ctx tipe =
375 doc = toDoc localizer ctx tipe
380similar :: L.Localizer -> RT.Context -> Type -> Type -> Diff D.Doc
381similar localizer ctx t1 t2 =
382 Diff (toDoc localizer ctx t1) (toDoc localizer ctx t2) Similar
385different :: a -> a -> Bag.Bag Problem -> Diff a
386different a b problems =
387 Diff a b (Different problems)
390isSimilar :: Diff a -> Bool
391isSimilar (Diff _ _ status) =
401isBool :: ModuleName.Canonical -> Name.Name -> Bool
403 home == ModuleName.basics && name == Name.bool
406isInt :: ModuleName.Canonical -> Name.Name -> Bool
408 home == ModuleName.basics && name == Name.int
411isFloat :: ModuleName.Canonical -> Name.Name -> Bool
413 home == ModuleName.basics && name == Name.float
416isString :: ModuleName.Canonical -> Name.Name -> Bool
418 home == ModuleName.string && name == Name.string
421isChar :: ModuleName.Canonical -> Name.Name -> Bool
423 home == ModuleName.char && name == Name.char
426isMaybe :: ModuleName.Canonical -> Name.Name -> Bool
428 home == ModuleName.maybe && name == Name.maybe
431isList :: ModuleName.Canonical -> Name.Name -> Bool
433 home == ModuleName.list && name == Name.list
440isSuper :: Super -> Type -> Bool
442 case iteratedDealias tipe of
445 Number -> isInt h n || isFloat h n
446 Comparable -> isInt h n || isFloat h n || isString h n || isChar h n || isList h n && isSuper super (head args)
447 Appendable -> isString h n || isList h n
448 CompAppend -> isString h n || isList h n && isSuper Comparable (head args)
458nameClashToDoc :: RT.Context -> L.Localizer -> ModuleName.Canonical -> Name.Name -> [Type] -> D.Doc
459nameClashToDoc ctx localizer (ModuleName.Canonical _ home) name args =
461 (D.yellow (D.fromName home) <> D.dullyellow ("." <> D.fromName name))
462 (map (toDoc localizer RT.App) args)
466-- DIFF ALIASED RECORD
469diffAliasedRecord :: L.Localizer -> Type -> Type -> Maybe (Diff D.Doc)
470diffAliasedRecord localizer t1 t2 =
471 case (iteratedDealias t1, iteratedDealias t2) of
472 (Record fields1 ext1, Record fields2 ext2) ->
473 Just (diffRecord localizer fields1 ext1 fields2 ext2)
485-- length cs1 /= length cs2
487diffLambda :: L.Localizer -> RT.Context -> Type -> Type -> [Type] -> Type -> Type -> [Type] -> Diff D.Doc
488diffLambda localizer ctx a1 b1 cs1 a2 b2 cs2 =
490 (result1:revArgs1) = reverse (a1:b1:cs1)
491 (result2:revArgs2) = reverse (a2:b2:cs2)
493 numArgs1 = length revArgs1
494 numArgs2 = length revArgs2
496 case toDiff localizer RT.Func result1 result2 of
497 Diff resultDoc1 resultDoc2 Similar ->
498 if numArgs1 < numArgs2 then
499 diffArgMismatch localizer ctx revArgs1 resultDoc1 revArgs2 resultDoc2
502 diffArgMismatch localizer ctx revArgs2 resultDoc2 revArgs1 resultDoc1
504 Diff resultDoc1 resultDoc2 (Different problems) ->
506 (a:b:cs) = reverse (resultDoc1 : map (toDoc localizer RT.Func) revArgs1)
507 (x:y:zs) = reverse (resultDoc2 : map (toDoc localizer RT.Func) revArgs2)
510 (D.dullyellow (RT.lambda ctx a b cs))
511 (D.dullyellow (RT.lambda ctx x y zs))
512 (Bag.append problems (Bag.one ReturnMismatch))
517-- length shortRevArgs >= 2
518-- length longRevArgs >= 2
519-- length shortRevArgs < length longRevArgs
521diffArgMismatch :: L.Localizer -> RT.Context -> [Type] -> D.Doc -> [Type] -> D.Doc -> Diff D.Doc
522diffArgMismatch localizer ctx shortRevArgs shortResult longRevArgs longResult =
525 case toGreedyMatch localizer shortRevArgs longRevArgs of
526 Just (GreedyMatch shortRevArgDocs longRevArgDocs) ->
527 ( reverse (shortResult:shortRevArgDocs)
528 , reverse (longResult:longRevArgDocs)
532 case toGreedyMatch localizer (reverse shortRevArgs) (reverse longRevArgs) of
533 Just (GreedyMatch shortArgDocs longArgDocs) ->
534 ( shortArgDocs ++ [shortResult]
535 , longArgDocs ++ [longResult]
541 D.dullyellow (toDoc localizer RT.Func tipe)
543 ( reverse (shortResult : map toYellowDoc shortRevArgs)
544 , reverse (longResult : map toYellowDoc longRevArgs )
548 (RT.lambda ctx a b cs)
549 (RT.lambda ctx x y zs)
550 (Bag.one (MissingArgs (length longRevArgs - length shortRevArgs)))
558 GreedyMatch [D.Doc] [D.Doc]
563-- length shorterArgs < length longerArgs
565toGreedyMatch :: L.Localizer -> [Type] -> [Type] -> Maybe GreedyMatch
566toGreedyMatch localizer shorterArgs longerArgs =
567 toGreedyMatchHelp localizer shorterArgs longerArgs (GreedyMatch [] [])
570toGreedyMatchHelp :: L.Localizer -> [Type] -> [Type] -> GreedyMatch -> Maybe GreedyMatch
571toGreedyMatchHelp localizer shorterArgs longerArgs match@(GreedyMatch shorterDocs longerDocs) =
574 D.dullyellow (toDoc localizer RT.Func tipe)
576 case (shorterArgs, longerArgs) of
578 case toDiff localizer RT.Func x y of
580 toGreedyMatchHelp localizer xs ys $
581 GreedyMatch (a:shorterDocs) (b:longerDocs)
583 Diff _ _ (Different _) ->
584 toGreedyMatchHelp localizer shorterArgs ys $
585 GreedyMatch shorterDocs (toYellowDoc y : longerDocs)
591 Just (GreedyMatch shorterDocs (map toYellowDoc longerArgs))
601diffRecord :: L.Localizer -> Map.Map Name.Name Type -> Extension -> Map.Map Name.Name Type -> Extension -> Diff D.Doc
602diffRecord localizer fields1 ext1 fields2 ext2 =
604 toUnknownDocs field tipe =
605 ( D.dullyellow (D.fromName field), toDoc localizer RT.None tipe )
607 toOverlapDocs field t1 t2 =
608 (,) (D.fromName field) <$> toDiff localizer RT.None t1 t2
610 left = Map.mapWithKey toUnknownDocs (Map.difference fields1 fields2)
611 both = Map.intersectionWithKey toOverlapDocs fields1 fields2
612 right = Map.mapWithKey toUnknownDocs (Map.difference fields2 fields1)
616 if Map.null left && Map.null right then
621 <*> Diff left right (Different Bag.empty)
623 (Diff doc1 doc2 status) =
626 <*> extToDiff ext1 ext2
628 Diff doc1 doc2 $ merge status $
629 case (hasFixedFields ext1, hasFixedFields ext2) of
631 case Map.lookupMin left of
632 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields2)
636 else Different $ Bag.one $ FieldsMissing (Map.keys right)
639 case Map.lookupMin left of
640 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields2)
644 case Map.lookupMin right of
645 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields1)
652hasFixedFields :: Extension -> Bool
661-- DIFF RECORD EXTENSION
664extToDiff :: Extension -> Extension -> Diff (Maybe D.Doc)
667 status = extToStatus ext1 ext2
668 extDoc1 = extToDoc ext1
669 extDoc2 = extToDoc ext2
673 Diff extDoc1 extDoc2 status
676 Diff (D.dullyellow <$> extDoc1) (D.dullyellow <$> extDoc2) status
679extToStatus :: Extension -> Extension -> Status
680extToStatus ext1 ext2 =
685 FlexOpen _ -> Similar
686 RigidOpen _ -> Different Bag.empty
693 Closed -> Different Bag.empty
694 FlexOpen _ -> Similar
698 else Different $ Bag.one $ BadRigidVar x (RigidVar y)
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