1{-# OPTIONS_GHC -Wall #-}
2module Transform.Check (mistakes) where
4import qualified Data.List as List
5import qualified Data.Maybe as Maybe
6import qualified Data.Set as Set
8import qualified AST.Expression.Valid as Valid
9import qualified AST.Declaration as D
10import qualified AST.Pattern as Pattern
11import qualified AST.Type as T
12import qualified AST.Variable as Var
13import qualified Transform.Expression as Expr
14import qualified Transform.Canonicalize.Setup as Setup
17import Elm.Utils ((|>))
18import Text.PrettyPrint as P
21mistakes :: [D.ValidDecl] -> [Doc]
24 [ infiniteTypeAliases decls
25 , illFormedTypeDecls decls
26 , map P.text (duplicateTypeDeclarations decls)
27 , map P.text (duplicateValues decls)
33dups :: Ord a => [a] -> [a]
37 |> filter ((>1) . length)
41duplicateValues :: [D.ValidDecl] -> [String]
42duplicateValues decls =
43 map msg (dups (portNames ++ concatMap Pattern.boundVarList defPatterns)) ++
44 case mapM exprDups defExprs of
45 Left name -> [msg name]
50 "Name Collision: There can only be one definition of '" ++ x ++ "'."
52 (defPatterns, defExprs) =
53 unzip [ (pat,expr) | D.Definition (Valid.Definition pat expr _) <- decls ]
56 [ D.getName port | D.Port port <- decls ]
58 exprDups :: Valid.Expr -> Either String Valid.Expr
60 Expr.crawlLet defsDups expr
62 defsDups :: [Valid.Def] -> Either String [Valid.Def]
64 let varsIn (Valid.Definition pattern _ _) = Pattern.boundVarList pattern in
65 case dups $ concatMap varsIn defs of
70duplicateTypeDeclarations :: [D.ValidDecl] -> [String]
71duplicateTypeDeclarations decls =
72 map dupTypeError (dups (typeNames ++ aliasNames))
73 ++ map dupCtorError (dups (ctorNames ++ aliasNames))
76 [ name | D.Datatype name _ _ <- decls ]
79 [ name | D.TypeAlias name _ _ <- decls ]
82 concat [ map fst patterns | D.Datatype _ _ patterns <- decls ]
85dupTypeError :: String -> String
87 "Name Collision: There can only be one type named '" ++ name ++ "'"
90dupCtorError :: String -> String
92 "Name Collision: There can only be one constructor named '" ++ name ++ "'.\n"
93 ++ " Constructors are created for record type aliases and for union types, so\n"
94 ++ " something should be renamed or moved to a different module."
97-- FREE TYPE VARIABLES IN TYPE DECLARATIONS
99illFormedTypeDecls :: [D.ValidDecl] -> [Doc]
100illFormedTypeDecls decls =
101 map report (Maybe.mapMaybe isIllFormed (aliases ++ adts))
104 [ (decl, tvars, [tipe]) | decl@(D.TypeAlias _ tvars tipe) <- decls ]
107 [ (decl, tvars, concatMap snd ctors) | decl@(D.Datatype _ tvars ctors) <- decls ]
112 Set.union (freeVars t1) (freeVars t2)
121 Set.unions (map freeVars (t:ts))
123 T.Record fields ext ->
124 let ext' = maybe Set.empty freeVars ext
126 Set.unions (ext' : map (freeVars . snd) fields)
128 T.Aliased _ args t ->
129 freeVars (T.dealias args t)
131 undeclared tvars tipes =
132 Set.difference used declared
134 used = Set.unions (map freeVars tipes)
135 declared = Set.fromList tvars
137 isIllFormed (decl, tvars, tipes) =
138 let unbound = undeclared tvars tipes in
141 else Just (decl, Set.toList unbound)
143 report (decl, tvars) =
144 P.vcat [ P.text $ "Error: type variable" ++ listing ++ " unbound in:"
146 , nest 4 (pretty decl) ]
150 [tvar] -> " " ++ quote tvar ++ " is"
151 _ -> "s" ++ addCommas (map ((++) " ") (addAnd (map quote tvars))) ++ " are"
154 | length xs < 3 = concat xs
155 | otherwise = List.intercalate "," xs
159 | otherwise = zipWith (++) (replicate (length xs - 1) "" ++ ["and "]) xs
161 quote tvar = "'" ++ tvar ++ "'"
164-- INFINITE TYPE ALIASES
166infiniteTypeAliases :: [D.ValidDecl] -> [Doc]
167infiniteTypeAliases decls =
168 [ report name tvars tipe
169 | D.TypeAlias name tvars tipe <- decls
170 , infiniteType name tipe
173 infiniteType :: String -> T.Type Var.Raw -> Bool
174 infiniteType name tipe =
175 let infinite = infiniteType name in
178 infinite a || infinite b
183 T.Type (Var.Raw name') ->
190 any (infinite . snd) fields
192 T.Aliased _ args aliasType ->
195 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