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 , illFormedTypeDecls decls
27 , map P.text (duplicateTypeDeclarations decls)
28 , map P.text (duplicateValues decls)
34dups :: Ord a => [a] -> [a]
38 |> filter ((>1) . length)
42duplicateValues :: [D.ValidDecl] -> [String]
43duplicateValues decls =
44 map msg (dups (portNames ++ concatMap Pattern.boundVarList defPatterns)) ++
45 case mapM exprDups (portExprs ++ defExprs) of
46 Left name -> [msg name]
51 "Name Collision: There can only be one definition of '" ++ x ++ "'."
53 (defPatterns, defExprs) =
54 unzip [ (pat,expr) | D.Definition (Valid.Definition pat expr _) <- decls ]
56 (portNames, portExprs) =
57 Arrow.second Maybe.catMaybes $ unzip $
58 flip map [ wire | D.Wire wire <- decls ] $ \wire ->
60 D.Input name _ -> (name, Nothing)
61 D.Output name expr _ -> (name, Just expr)
62 D.Loopback name maybeExpr _ -> (name, maybeExpr)
64 exprDups :: Valid.Expr -> Either String Valid.Expr
65 exprDups expr = Expr.crawlLet defsDups expr
67 defsDups :: [Valid.Def] -> Either String [Valid.Def]
69 let varsIn (Valid.Definition pattern _ _) = Pattern.boundVarList pattern in
70 case dups $ concatMap varsIn defs of
75duplicateTypeDeclarations :: [D.ValidDecl] -> [String]
76duplicateTypeDeclarations decls =
77 map dupTypeError (dups (typeNames ++ aliasNames))
78 ++ map dupCtorError (dups (ctorNames ++ aliasNames))
81 [ name | D.Datatype name _ _ <- decls ]
84 [ name | D.TypeAlias name _ _ <- decls ]
87 concat [ map fst patterns | D.Datatype _ _ patterns <- decls ]
90dupTypeError :: String -> String
92 "Name Collision: There can only be one type named '" ++ name ++ "'"
95dupCtorError :: String -> String
97 "Name Collision: There can only be one constructor named '" ++ name ++ "'.\n"
98 ++ " Constructors are created for record type aliases and for union types, so\n"
99 ++ " something should be renamed or moved to a different module."
102-- FREE TYPE VARIABLES IN TYPE DECLARATIONS
104illFormedTypeDecls :: [D.ValidDecl] -> [Doc]
105illFormedTypeDecls decls =
106 map report (Maybe.mapMaybe isIllFormed (aliases ++ adts))
109 [ (decl, tvars, [tipe]) | decl@(D.TypeAlias _ tvars tipe) <- decls ]
112 [ (decl, tvars, concatMap snd ctors) | decl@(D.Datatype _ tvars ctors) <- decls ]
117 Set.union (freeVars t1) (freeVars t2)
126 Set.unions (map freeVars (t:ts))
128 T.Record fields ext ->
129 let ext' = maybe Set.empty freeVars ext
131 Set.unions (ext' : map (freeVars . snd) fields)
133 T.Aliased _ args t ->
134 freeVars (T.dealias args t)
136 undeclared tvars tipes =
137 Set.difference used declared
139 used = Set.unions (map freeVars tipes)
140 declared = Set.fromList tvars
142 isIllFormed (decl, tvars, tipes) =
143 let unbound = undeclared tvars tipes in
146 else Just (decl, Set.toList unbound)
148 report (decl, tvars) =
149 P.vcat [ P.text $ "Error: type variable" ++ listing ++ " unbound in:"
151 , nest 4 (pretty decl) ]
155 [tvar] -> " " ++ quote tvar ++ " is"
156 _ -> "s" ++ addCommas (map ((++) " ") (addAnd (map quote tvars))) ++ " are"
159 | length xs < 3 = concat xs
160 | otherwise = List.intercalate "," xs
164 | otherwise = zipWith (++) (replicate (length xs - 1) "" ++ ["and "]) xs
166 quote tvar = "'" ++ tvar ++ "'"
169-- INFINITE TYPE ALIASES
171infiniteTypeAliases :: [D.ValidDecl] -> [Doc]
172infiniteTypeAliases decls =
173 [ report name tvars tipe
174 | D.TypeAlias name tvars tipe <- decls
175 , infiniteType name tipe
178 infiniteType :: String -> T.Type Var.Raw -> Bool
179 infiniteType name tipe =
180 let infinite = infiniteType name in
183 infinite a || infinite b
188 T.Type (Var.Raw name') ->
195 any (infinite . snd) fields
197 T.Aliased _ args t ->
198 infinite t || any (infinite . snd) args
200 indented :: D.ValidDecl -> Doc
202 P.text "\n " <> pretty decl <> P.text "\n"
204 report name args tipe =
206 [ P.text $ eightyCharLines 0 msg1
207 , indented $ D.TypeAlias name args tipe
208 , P.text $ eightyCharLines 0 Setup.typeAliasErrorSegue
209 , indented $ D.Datatype name args [(name,[tipe])]
210 , P.text $ eightyCharLines 0 Setup.typeAliasErrorExplanation ++ "\n"
214 "Type alias '" ++ name ++ "' is an infinite type. " ++
215 "Notice that it appears in its own definition, so when \
216 \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