1{-# OPTIONS_GHC -Wall #-}
2module Transform.Check (mistakes) where
4import qualified Control.Arrow as Arrow
5import qualified Data.List as List
6import qualified Data.Maybe as Maybe
7import qualified Data.Set as Set
9import qualified AST.Expression.Valid as Valid
10import qualified AST.Declaration as D
11import qualified AST.Pattern as Pattern
12import qualified AST.Type as T
13import qualified AST.Variable as Var
14import qualified Transform.Expression as Expr
15import qualified Transform.Canonicalize.Setup as Setup
18import Elm.Utils ((|>))
19import Text.PrettyPrint as P
22mistakes :: [D.ValidDecl] -> [Doc]
25 [ infiniteTypeAliases decls
26 , illFormedTypes decls
27 , map P.text (duplicateTypeDeclarations decls)
28 , map P.text (duplicateValues decls)
32dups :: Ord a => [a] -> [a]
36 |> filter ((>1) . length)
40duplicateValues :: [D.ValidDecl] -> [String]
41duplicateValues decls =
42 map msg (dups (portNames ++ concatMap Pattern.boundVarList defPatterns)) ++
43 case mapM exprDups (portExprs ++ defExprs) of
44 Left name -> [msg name]
49 "Name Collision: There can only be one definition of '" ++ x ++ "'."
51 (defPatterns, defExprs) =
52 unzip [ (pat,expr) | D.Definition (Valid.Definition pat expr _) <- decls ]
54 (portNames, portExprs) =
55 Arrow.second Maybe.catMaybes $ unzip $
56 flip map [ wire | D.Wire wire <- decls ] $ \wire ->
58 D.Input name _ -> (name, Nothing)
59 D.Output name expr _ -> (name, Just expr)
60 D.Loopback name maybeExpr _ -> (name, maybeExpr)
62 exprDups :: Valid.Expr -> Either String Valid.Expr
63 exprDups expr = Expr.crawlLet defsDups expr
65 defsDups :: [Valid.Def] -> Either String [Valid.Def]
67 let varsIn (Valid.Definition pattern _ _) = Pattern.boundVarList pattern in
68 case dups $ concatMap varsIn defs of
73duplicateTypeDeclarations :: [D.ValidDecl] -> [String]
74duplicateTypeDeclarations decls =
75 map dupTypeError (dups (typeNames ++ aliasNames))
76 ++ map dupCtorError (dups (ctorNames ++ aliasNames))
79 [ name | D.Datatype name _ _ <- decls ]
82 [ name | D.TypeAlias name _ _ <- decls ]
85 concat [ map fst patterns | D.Datatype _ _ patterns <- decls ]
88dupTypeError :: String -> String
90 "Name Collision: There can only be one type named '" ++ name ++ "'"
93dupCtorError :: String -> String
95 "Name Collision: There can only be one constructor named '" ++ name ++ "'.\n"
96 ++ " Constructors are created for record type aliases and for union types, so\n"
97 ++ " something should be renamed or moved to a different module."
100illFormedTypes :: [D.ValidDecl] -> [Doc]
101illFormedTypes decls = map report (Maybe.mapMaybe isIllFormed (aliases ++ adts))
103 aliases = [ (decl, tvars, [tipe]) | decl@(D.TypeAlias _ tvars tipe) <- decls ]
104 adts = [ (decl, tvars, concatMap snd ctors) | decl@(D.Datatype _ tvars ctors) <- decls ]
108 T.Lambda t1 t2 -> Set.union (freeVars t1) (freeVars t2)
109 T.Var x -> Set.singleton x
110 T.Type _ -> Set.empty
111 T.App t ts -> Set.unions (map freeVars (t:ts))
112 T.Record fields ext -> Set.unions (ext' : map (freeVars . snd) fields)
113 where ext' = maybe Set.empty freeVars ext
114 T.Aliased _ t -> freeVars t
116 undeclared tvars tipes = Set.difference used declared
118 used = Set.unions (map freeVars tipes)
119 declared = Set.fromList tvars
121 isIllFormed (decl, tvars, tipes) =
122 let unbound = undeclared tvars tipes in
123 if Set.null unbound then Nothing
124 else Just (decl, Set.toList unbound)
126 report (decl, tvars) =
127 P.vcat [ P.text $ "Error: type variable" ++ listing ++ " unbound in:"
129 , nest 4 (pretty decl) ]
133 [tvar] -> " " ++ quote tvar ++ " is"
134 _ -> "s" ++ addCommas (map ((++) " ") (addAnd (map quote tvars))) ++ " are"
137 | length xs < 3 = concat xs
138 | otherwise = List.intercalate "," xs
142 | otherwise = zipWith (++) (replicate (length xs - 1) "" ++ ["and "]) xs
144 quote tvar = "'" ++ tvar ++ "'"
147infiniteTypeAliases :: [D.ValidDecl] -> [Doc]
148infiniteTypeAliases decls =
149 [ report name tvars tipe | D.TypeAlias name tvars tipe <- decls
150 , infiniteType name tipe ]
152 infiniteType :: String -> T.Type Var.Raw -> Bool
153 infiniteType name tipe =
154 let infinite = infiniteType name in
156 T.Lambda a b -> infinite a || infinite b
158 T.Type (Var.Raw name') -> name == name'
159 T.App t ts -> any infinite (t:ts)
160 T.Record fields _ -> any (infinite . snd) fields
161 T.Aliased _ t -> infinite t
163 indented :: D.ValidDecl -> Doc
164 indented decl = P.text "\n " <> pretty decl <> P.text "\n"
166 report name args tipe =
168 [ P.text $ eightyCharLines 0 msg1
169 , indented $ D.TypeAlias name args tipe
170 , P.text $ eightyCharLines 0 Setup.typeAliasErrorSegue
171 , indented $ D.Datatype name args [(name,[tipe])]
172 , P.text $ eightyCharLines 0 Setup.typeAliasErrorExplanation ++ "\n"
176 "Type alias '" ++ name ++ "' is an infinite type. " ++
177 "Notice that it appears in its own definition, so when \
178 \you expand it, it just keeps getting bigger:"
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