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)
178 | ArityMismatch Int Int
179 | BadFlexSuper Direction Super Name.Name Type
180 | BadRigidVar Name.Name Type
181 | BadRigidSuper Super Name.Name Type
182 | FieldTypo Name.Name [Name.Name]
183 | FieldsMissing [Name.Name]
186data Direction = Have | Need
189instance Functor Diff where
190 fmap func (Diff a b status) =
191 Diff (func a) (func b) status
194instance Applicative Diff where
198 (<*>) (Diff aFunc bFunc status1) (Diff aArg bArg status2) =
199 Diff (aFunc aArg) (bFunc bArg) (merge status1 status2)
202merge :: Status -> Status -> Status
203merge status1 status2 =
208 Different problems1 ->
213 Different problems2 ->
214 Different (Bag.append problems1 problems2)
221toComparison :: L.Localizer -> Type -> Type -> (D.Doc, D.Doc, [Problem])
222toComparison localizer tipe1 tipe2 =
223 case toDiff localizer RT.None tipe1 tipe2 of
224 Diff doc1 doc2 Similar ->
227 Diff doc1 doc2 (Different problems) ->
228 (doc1, doc2, Bag.toList problems)
231toDiff :: L.Localizer -> RT.Context -> Type -> Type -> Diff D.Doc
232toDiff localizer ctx tipe1 tipe2 =
233 case (tipe1, tipe2) of
234 (Unit , Unit ) -> same localizer ctx tipe1
235 (Error , Error ) -> same localizer ctx tipe1
236 (Infinite, Infinite) -> same localizer ctx tipe1
238 (FlexVar x, FlexVar y) | x == y -> same localizer ctx tipe1
239 (FlexSuper _ x, FlexSuper _ y) | x == y -> same localizer ctx tipe1
240 (RigidVar x, RigidVar y) | x == y -> same localizer ctx tipe1
241 (RigidSuper _ x, RigidSuper _ y) | x == y -> same localizer ctx tipe1
243 (FlexVar _, _ ) -> similar localizer ctx tipe1 tipe2
244 (_ , FlexVar _) -> similar localizer ctx tipe1 tipe2
246 (FlexSuper s _, t ) | isSuper s t -> similar localizer ctx tipe1 tipe2
247 (t , FlexSuper s _) | isSuper s t -> similar localizer ctx tipe1 tipe2
249 (Lambda a b cs, Lambda x y zs) ->
250 if length cs == length zs then
252 <$> toDiff localizer RT.Func a x
253 <*> toDiff localizer RT.Func b y
254 <*> sequenceA (zipWith (toDiff localizer RT.Func) cs zs)
256 let f = toDoc localizer RT.Func in
258 (D.dullyellow (RT.lambda ctx (f a) (f b) (map f cs)))
259 (D.dullyellow (RT.lambda ctx (f x) (f y) (map f zs)))
260 (Bag.one (ArityMismatch (2 + length cs) (2 + length zs)))
262 (Tuple a b Nothing, Tuple x y Nothing) ->
264 <$> toDiff localizer RT.None a x
265 <*> toDiff localizer RT.None b y
268 (Tuple a b (Just c), Tuple x y (Just z)) ->
270 <$> toDiff localizer RT.None a x
271 <*> toDiff localizer RT.None b y
272 <*> ((:[]) <$> toDiff localizer RT.None c z)
274 (Record fields1 ext1, Record fields2 ext2) ->
275 diffRecord localizer fields1 ext1 fields2 ext2
277 (Type home1 name1 args1, Type home2 name2 args2) | home1 == home2 && name1 == name2 ->
278 RT.apply ctx (L.toDoc localizer home1 name1)
279 <$> sequenceA (zipWith (toDiff localizer RT.App) args1 args2)
281 (Alias home1 name1 args1 _, Alias home2 name2 args2 _) | home1 == home2 && name1 == name2 ->
282 RT.apply ctx (L.toDoc localizer home1 name1)
283 <$> sequenceA (zipWith (toDiff localizer RT.App) (map snd args1) (map snd args2))
285 -- start trying to find specific problems
287 (Type home1 name1 args1, Type home2 name2 args2) | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
289 (nameClashToDoc ctx localizer home1 name1 args1)
290 (nameClashToDoc ctx localizer home2 name2 args2)
293 (Type home name [t1], t2) | isMaybe home name && isSimilar (toDiff localizer ctx t1 t2) ->
295 (RT.apply ctx (D.dullyellow (L.toDoc localizer home name)) [toDoc localizer RT.App t1])
296 (toDoc localizer ctx t2)
297 (Bag.one AnythingFromMaybe)
299 (t1, Type home name [t2]) | isList home name && isSimilar (toDiff localizer ctx t1 t2) ->
301 (toDoc localizer ctx t1)
302 (RT.apply ctx (D.dullyellow (L.toDoc localizer home name)) [toDoc localizer RT.App t2])
305 (Alias home1 name1 args1 t1, t2) ->
306 case diffAliasedRecord localizer t1 t2 of
307 Just (Diff _ doc2 status) ->
308 Diff (D.dullyellow (aliasToDoc localizer ctx home1 name1 args1)) doc2 status
312 Type home2 name2 args2 | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
314 (nameClashToDoc ctx localizer home1 name1 (map snd args1))
315 (nameClashToDoc ctx localizer home2 name2 args2)
320 (D.dullyellow (toDoc localizer ctx tipe1))
321 (D.dullyellow (toDoc localizer ctx tipe2))
324 (t1, Alias home2 name2 args2 t2) ->
325 case diffAliasedRecord localizer t1 t2 of
326 Just (Diff doc1 _ status) ->
327 Diff doc1 (D.dullyellow (aliasToDoc localizer ctx home2 name2 args2)) status
331 Type home1 name1 args1 | L.toChars localizer home1 name1 == L.toChars localizer home2 name2 ->
333 (nameClashToDoc ctx localizer home1 name1 args1)
334 (nameClashToDoc ctx localizer home2 name2 (map snd args2))
339 (D.dullyellow (toDoc localizer ctx tipe1))
340 (D.dullyellow (toDoc localizer ctx tipe2))
345 doc1 = D.dullyellow (toDoc localizer ctx tipe1)
346 doc2 = D.dullyellow (toDoc localizer ctx tipe2)
348 different doc1 doc2 $
350 (RigidVar x, other) -> Bag.one $ BadRigidVar x other
351 (FlexSuper s x, other) -> Bag.one $ BadFlexSuper Have s x other
352 (RigidSuper s x, other) -> Bag.one $ BadRigidSuper s x other
353 (other, RigidVar x) -> Bag.one $ BadRigidVar x other
354 (other, FlexSuper s x) -> Bag.one $ BadFlexSuper Need s x other
355 (other, RigidSuper s x) -> Bag.one $ BadRigidSuper s x other
357 (Type home1 name1 [], Type home2 name2 [])
358 | isInt home1 name1 && isFloat home2 name2 -> Bag.one IntFloat
359 | isFloat home1 name1 && isInt home2 name2 -> Bag.one IntFloat
360 | isInt home1 name1 && isString home2 name2 -> Bag.one StringFromInt
361 | isFloat home1 name1 && isString home2 name2 -> Bag.one StringFromFloat
362 | isString home1 name1 && isInt home2 name2 -> Bag.one StringToInt
363 | isString home1 name1 && isFloat home2 name2 -> Bag.one StringToFloat
364 | isBool home2 name2 -> Bag.one AnythingToBool
374same :: L.Localizer -> RT.Context -> Type -> Diff D.Doc
375same localizer ctx tipe =
377 doc = toDoc localizer ctx tipe
382similar :: L.Localizer -> RT.Context -> Type -> Type -> Diff D.Doc
383similar localizer ctx t1 t2 =
384 Diff (toDoc localizer ctx t1) (toDoc localizer ctx t2) Similar
387different :: a -> a -> Bag.Bag Problem -> Diff a
388different a b problems =
389 Diff a b (Different problems)
392isSimilar :: Diff a -> Bool
393isSimilar (Diff _ _ status) =
403isBool :: ModuleName.Canonical -> Name.Name -> Bool
405 home == ModuleName.basics && name == Name.bool
408isInt :: ModuleName.Canonical -> Name.Name -> Bool
410 home == ModuleName.basics && name == Name.int
413isFloat :: ModuleName.Canonical -> Name.Name -> Bool
415 home == ModuleName.basics && name == Name.float
418isString :: ModuleName.Canonical -> Name.Name -> Bool
420 home == ModuleName.string && name == Name.string
423isChar :: ModuleName.Canonical -> Name.Name -> Bool
425 home == ModuleName.char && name == Name.char
428isMaybe :: ModuleName.Canonical -> Name.Name -> Bool
430 home == ModuleName.maybe && name == Name.maybe
433isList :: ModuleName.Canonical -> Name.Name -> Bool
435 home == ModuleName.list && name == Name.list
442isSuper :: Super -> Type -> Bool
444 case iteratedDealias tipe of
447 Number -> isInt h n || isFloat h n
448 Comparable -> isInt h n || isFloat h n || isString h n || isChar h n || isList h n && isSuper super (head args)
449 Appendable -> isString h n || isList h n
450 CompAppend -> isString h n || isList h n && isSuper Comparable (head args)
455 Comparable -> isSuper super a && isSuper super b && maybe True (isSuper super) maybeC
467nameClashToDoc :: RT.Context -> L.Localizer -> ModuleName.Canonical -> Name.Name -> [Type] -> D.Doc
468nameClashToDoc ctx localizer (ModuleName.Canonical _ home) name args =
470 (D.yellow (D.fromName home) <> D.dullyellow ("." <> D.fromName name))
471 (map (toDoc localizer RT.App) args)
475-- DIFF ALIASED RECORD
478diffAliasedRecord :: L.Localizer -> Type -> Type -> Maybe (Diff D.Doc)
479diffAliasedRecord localizer t1 t2 =
480 case (iteratedDealias t1, iteratedDealias t2) of
481 (Record fields1 ext1, Record fields2 ext2) ->
482 Just (diffRecord localizer fields1 ext1 fields2 ext2)
492diffRecord :: L.Localizer -> Map.Map Name.Name Type -> Extension -> Map.Map Name.Name Type -> Extension -> Diff D.Doc
493diffRecord localizer fields1 ext1 fields2 ext2 =
495 toUnknownDocs field tipe =
496 ( D.dullyellow (D.fromName field), toDoc localizer RT.None tipe )
498 toOverlapDocs field t1 t2 =
499 (,) (D.fromName field) <$> toDiff localizer RT.None t1 t2
501 left = Map.mapWithKey toUnknownDocs (Map.difference fields1 fields2)
502 both = Map.intersectionWithKey toOverlapDocs fields1 fields2
503 right = Map.mapWithKey toUnknownDocs (Map.difference fields2 fields1)
507 if Map.null left && Map.null right then
512 <*> Diff left right (Different Bag.empty)
514 (Diff doc1 doc2 status) =
517 <*> extToDiff ext1 ext2
519 Diff doc1 doc2 $ merge status $
520 case (hasFixedFields ext1, hasFixedFields ext2) of
522 case Map.lookupMin left of
523 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields2)
527 else Different $ Bag.one $ FieldsMissing (Map.keys right)
530 case Map.lookupMin left of
531 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields2)
535 case Map.lookupMin right of
536 Just (f,_) -> Different $ Bag.one $ FieldTypo f (Map.keys fields1)
543hasFixedFields :: Extension -> Bool
552-- DIFF RECORD EXTENSION
555extToDiff :: Extension -> Extension -> Diff (Maybe D.Doc)
558 status = extToStatus ext1 ext2
559 extDoc1 = extToDoc ext1
560 extDoc2 = extToDoc ext2
564 Diff extDoc1 extDoc2 status
567 Diff (D.dullyellow <$> extDoc1) (D.dullyellow <$> extDoc2) status
570extToStatus :: Extension -> Extension -> Status
571extToStatus ext1 ext2 =
576 FlexOpen _ -> Similar
577 RigidOpen _ -> Different Bag.empty
584 Closed -> Different Bag.empty
585 FlexOpen _ -> Similar
589 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