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)
179 | ArityMismatch Int Int
180 | BadFlexSuper Direction Super Name.Name Type
181 | BadRigidVar Name.Name Type
182 | BadRigidSuper Super Name.Name Type
183 | FieldTypo Name.Name [Name.Name]
184 | FieldsMissing [Name.Name]
187data Direction = Have | Need
190instance Functor Diff where
191 fmap func (Diff a b status) =
192 Diff (func a) (func b) status
195instance Applicative Diff where
199 (<*>) (Diff aFunc bFunc status1) (Diff aArg bArg status2) =
200 Diff (aFunc aArg) (bFunc bArg) (merge status1 status2)
203merge :: Status -> Status -> Status
204merge status1 status2 =
209 Different problems1 ->
214 Different problems2 ->
215 Different (Bag.append problems1 problems2)
222toComparison :: L.Localizer -> Type -> Type -> (D.Doc, D.Doc, [Problem])
223toComparison localizer tipe1 tipe2 =
224 case toDiff localizer RT.None tipe1 tipe2 of
225 Diff doc1 doc2 Similar ->
228 Diff doc1 doc2 (Different problems) ->
229 (doc1, doc2, Bag.toList problems)
232toDiff :: L.Localizer -> RT.Context -> Type -> Type -> Diff D.Doc
233toDiff localizer ctx tipe1 tipe2 =
234 case (tipe1, tipe2) of
235 (Unit , Unit ) -> same localizer ctx tipe1
236 (Error , Error ) -> same localizer ctx tipe1
237 (Infinite, Infinite) -> same localizer ctx tipe1
239 (FlexVar x, FlexVar y) | x == y -> same localizer ctx tipe1
240 (FlexSuper _ x, FlexSuper _ y) | x == y -> same localizer ctx tipe1
241 (RigidVar x, RigidVar y) | x == y -> same localizer ctx tipe1
242 (RigidSuper _ x, RigidSuper _ y) | x == y -> same localizer ctx tipe1
244 (FlexVar _, _ ) -> similar localizer ctx tipe1 tipe2
245 (_ , FlexVar _) -> similar localizer ctx tipe1 tipe2
247 (FlexSuper s _, t ) | isSuper s t -> similar localizer ctx tipe1 tipe2
248 (t , FlexSuper s _) | isSuper s t -> similar localizer ctx tipe1 tipe2
250 (Lambda a b cs, Lambda x y zs) ->
251 if length cs == length zs then
253 <$> toDiff localizer RT.Func a x
254 <*> toDiff localizer RT.Func b y
255 <*> sequenceA (zipWith (toDiff localizer RT.Func) cs zs)
257 let f = toDoc localizer RT.Func in
259 (D.dullyellow (RT.lambda ctx (f a) (f b) (map f cs)))
260 (D.dullyellow (RT.lambda ctx (f x) (f y) (map f zs)))
261 (Bag.one (ArityMismatch (2 + length cs) (2 + length zs)))
263 (Tuple a b Nothing, Tuple x y Nothing) ->
265 <$> toDiff localizer RT.None a x
266 <*> toDiff localizer RT.None b y
269 (Tuple a b (Just c), Tuple x y (Just z)) ->
271 <$> toDiff localizer RT.None a x
272 <*> toDiff localizer RT.None b y
273 <*> ((:[]) <$> toDiff localizer RT.None c z)
275 (Record fields1 ext1, Record fields2 ext2) ->
276 diffRecord localizer fields1 ext1 fields2 ext2
278 (Type home1 name1 args1, Type home2 name2 args2) | home1 == home2 && name1 == name2 ->
279 RT.apply ctx (L.toDoc localizer home1 name1)
280 <$> sequenceA (zipWith (toDiff localizer RT.App) args1 args2)
282 (Alias home1 name1 args1 _, Alias home2 name2 args2 _) | home1 == home2 && name1 == name2 ->
283 RT.apply ctx (L.toDoc localizer home1 name1)
284 <$> sequenceA (zipWith (toDiff localizer RT.App) (map snd args1) (map snd args2))
286 -- start trying to find specific problems
288 (Type home1 name1 args1, Type home2 name2 args2) | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
290 (nameClashToDoc ctx localizer home1 name1 args1)
291 (nameClashToDoc ctx localizer home2 name2 args2)
294 (Type home name [t1], t2) | isMaybe home name && isSimilar (toDiff localizer ctx t1 t2) ->
296 (RT.apply ctx (D.dullyellow (L.toDoc localizer home name)) [toDoc localizer RT.App t1])
297 (toDoc localizer ctx t2)
298 (Bag.one AnythingFromMaybe)
300 (t1, Type home name [t2]) | isList home name && isSimilar (toDiff localizer ctx t1 t2) ->
302 (toDoc localizer ctx t1)
303 (RT.apply ctx (D.dullyellow (L.toDoc localizer home name)) [toDoc localizer RT.App t2])
304 (Bag.one AnythingToList)
306 (Alias home1 name1 args1 t1, t2) ->
307 case diffAliasedRecord localizer t1 t2 of
308 Just (Diff _ doc2 status) ->
309 Diff (D.dullyellow (aliasToDoc localizer ctx home1 name1 args1)) doc2 status
313 Type home2 name2 args2 | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
315 (nameClashToDoc ctx localizer home1 name1 (map snd args1))
316 (nameClashToDoc ctx localizer home2 name2 args2)
321 (D.dullyellow (toDoc localizer ctx tipe1))
322 (D.dullyellow (toDoc localizer ctx tipe2))
325 (t1, Alias home2 name2 args2 t2) ->
326 case diffAliasedRecord localizer t1 t2 of
327 Just (Diff doc1 _ status) ->
328 Diff doc1 (D.dullyellow (aliasToDoc localizer ctx home2 name2 args2)) status
332 Type home1 name1 args1 | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
334 (nameClashToDoc ctx localizer home1 name1 args1)
335 (nameClashToDoc ctx localizer home2 name2 (map snd args2))
340 (D.dullyellow (toDoc localizer ctx tipe1))
341 (D.dullyellow (toDoc localizer ctx tipe2))
346 doc1 = D.dullyellow (toDoc localizer ctx tipe1)
347 doc2 = D.dullyellow (toDoc localizer ctx tipe2)
349 different doc1 doc2 $
351 (RigidVar x, other) -> Bag.one $ BadRigidVar x other
352 (FlexSuper s x, other) -> Bag.one $ BadFlexSuper Have s x other
353 (RigidSuper s x, other) -> Bag.one $ BadRigidSuper s x other
354 (other, RigidVar x) -> Bag.one $ BadRigidVar x other
355 (other, FlexSuper s x) -> Bag.one $ BadFlexSuper Need s x other
356 (other, RigidSuper s x) -> Bag.one $ BadRigidSuper s x other
358 (Type home1 name1 [], Type home2 name2 [])
359 | isInt home1 name1 && isFloat home2 name2 -> Bag.one IntFloat
360 | isFloat home1 name1 && isInt home2 name2 -> Bag.one IntFloat
361 | isInt home1 name1 && isString home2 name2 -> Bag.one StringFromInt
362 | isFloat home1 name1 && isString home2 name2 -> Bag.one StringFromFloat
363 | isString home1 name1 && isInt home2 name2 -> Bag.one StringToInt
364 | isString home1 name1 && isFloat home2 name2 -> Bag.one StringToFloat
365 | isBool home2 name2 -> Bag.one AnythingToBool
375same :: L.Localizer -> RT.Context -> Type -> Diff D.Doc
376same localizer ctx tipe =
378 doc = toDoc localizer ctx tipe
383similar :: L.Localizer -> RT.Context -> Type -> Type -> Diff D.Doc
384similar localizer ctx t1 t2 =
385 Diff (toDoc localizer ctx t1) (toDoc localizer ctx t2) Similar
388different :: a -> a -> Bag.Bag Problem -> Diff a
389different a b problems =
390 Diff a b (Different problems)
393isSimilar :: Diff a -> Bool
394isSimilar (Diff _ _ status) =
404isBool :: ModuleName.Canonical -> Name.Name -> Bool
406 home == ModuleName.basics && name == Name.bool
409isInt :: ModuleName.Canonical -> Name.Name -> Bool
411 home == ModuleName.basics && name == Name.int
414isFloat :: ModuleName.Canonical -> Name.Name -> Bool
416 home == ModuleName.basics && name == Name.float
419isString :: ModuleName.Canonical -> Name.Name -> Bool
421 home == ModuleName.string && name == Name.string
424isChar :: ModuleName.Canonical -> Name.Name -> Bool
426 home == ModuleName.char && name == Name.char
429isMaybe :: ModuleName.Canonical -> Name.Name -> Bool
431 home == ModuleName.maybe && name == Name.maybe
434isList :: ModuleName.Canonical -> Name.Name -> Bool
436 home == ModuleName.list && name == Name.list
443isSuper :: Super -> Type -> Bool
445 case iteratedDealias tipe of
448 Number -> isInt h n || isFloat h n
449 Comparable -> isInt h n || isFloat h n || isString h n || isChar h n || isList h n && isSuper super (head args)
450 Appendable -> isString h n || isList h n
451 CompAppend -> isString h n || isList h n && isSuper Comparable (head args)
461nameClashToDoc :: RT.Context -> L.Localizer -> ModuleName.Canonical -> Name.Name -> [Type] -> D.Doc
462nameClashToDoc ctx localizer (ModuleName.Canonical _ home) name args =
464 (D.yellow (D.fromName home) <> D.dullyellow ("." <> D.fromName name))
465 (map (toDoc localizer RT.App) args)
469-- DIFF ALIASED RECORD
472diffAliasedRecord :: L.Localizer -> Type -> Type -> Maybe (Diff D.Doc)
473diffAliasedRecord localizer t1 t2 =
474 case (iteratedDealias t1, iteratedDealias t2) of
475 (Record fields1 ext1, Record fields2 ext2) ->
476 Just (diffRecord localizer fields1 ext1 fields2 ext2)
486diffRecord :: L.Localizer -> Map.Map Name.Name Type -> Extension -> Map.Map Name.Name Type -> Extension -> Diff D.Doc
487diffRecord localizer fields1 ext1 fields2 ext2 =
489 toUnknownDocs field tipe =
490 ( D.dullyellow (D.fromName field), toDoc localizer RT.None tipe )
492 toOverlapDocs field t1 t2 =
493 (,) (D.fromName field) <$> toDiff localizer RT.None t1 t2
495 left = Map.mapWithKey toUnknownDocs (Map.difference fields1 fields2)
496 both = Map.intersectionWithKey toOverlapDocs fields1 fields2
497 right = Map.mapWithKey toUnknownDocs (Map.difference fields2 fields1)
501 if Map.null left && Map.null right then
506 <*> Diff left right (Different Bag.empty)
508 (Diff doc1 doc2 status) =
511 <*> extToDiff ext1 ext2
513 Diff doc1 doc2 $ merge status $
514 case (hasFixedFields ext1, hasFixedFields ext2) of
516 case Map.lookupMin left of
517 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields2)
521 else Different $ Bag.one $ FieldsMissing (Map.keys right)
524 case Map.lookupMin left of
525 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields2)
529 case Map.lookupMin right of
530 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields1)
537hasFixedFields :: Extension -> Bool
546-- DIFF RECORD EXTENSION
549extToDiff :: Extension -> Extension -> Diff (Maybe D.Doc)
552 status = extToStatus ext1 ext2
553 extDoc1 = extToDoc ext1
554 extDoc2 = extToDoc ext2
558 Diff extDoc1 extDoc2 status
561 Diff (D.dullyellow <$> extDoc1) (D.dullyellow <$> extDoc2) status
564extToStatus :: Extension -> Extension -> Status
565extToStatus ext1 ext2 =
570 FlexOpen _ -> Similar
571 RigidOpen _ -> Different Bag.empty
578 Closed -> Different Bag.empty
579 FlexOpen _ -> Similar
583 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