1{-# OPTIONS_GHC -Wall #-}
2module Validate (Result, module') where
4import Control.Monad (foldM_, when)
5import qualified Data.Map as Map
6import qualified Data.Maybe as Maybe
7import qualified Data.Set as Set
8import qualified Data.Foldable as F
9import qualified Data.Traversable as T
11import AST.Expression.General as Expr
12import qualified AST.Effects as Effects
13import qualified AST.Expression.Source as Source
14import qualified AST.Expression.Valid as Valid
15import qualified AST.Declaration as D
16import qualified AST.Module as Module
17import qualified AST.Module.Name as ModuleName
18import qualified AST.Pattern as Pattern
19import qualified AST.Type as Type
20import qualified Elm.Compiler.Imports as Imports
21import qualified Elm.Package as Package
22import Elm.Utils ((|>))
23import qualified Reporting.Annotation as A
24import qualified Reporting.Error.Syntax as Error
25import qualified Reporting.Region as R
26import qualified Reporting.Result as Result
30type Result warning a =
31 Result.Result () warning Error.Error a
38module' :: Module.Source -> Result wrn Module.Valid
39module' (Module.Module name path info) =
41 (ModuleName.Canonical pkgName _) =
44 (Module.Source tag settings docs exports imports decls) =
47 do (ValidStuff ports structure) <- validateDecls decls
49 validEffects <- validateEffects tag settings ports (D._defs structure)
51 return $ Module.Module name path $
52 Module.Valid docs exports (addDefaults pkgName imports) structure validEffects
61 -> [Module.UserImport]
62 -> ([Module.DefaultImport], [Module.UserImport])
63addDefaults pkgName imports =
65 if pkgName == Package.core then
78 -> Module.SourceSettings
79 -> [A.Commented Effects.PortRaw]
80 -> [A.Commented Valid.Def]
81 -> Result wrn Effects.Raw
82validateEffects tag settings@(A.A _ pairs) ports validDefs =
85 do noSettings Error.SettingsOnNormalModule settings
90 do noSettings Error.SettingsOnPortModule settings
91 return (Effects.Port ports)
93 Module.Effect tagRegion ->
95 collectSettings (A.A region setting, userValue) dict =
96 Map.insertWith (++) setting [(region, userValue)] dict
99 foldr collectSettings Map.empty pairs
102 managerType <- toManagerType tagRegion settingsDict
103 (r0, r1, r2) <- checkManager tagRegion managerType validDefs
104 return (Effects.Manager () (Effects.Info tagRegion r0 r1 r2 managerType))
107noSettings :: Error.Error -> Module.SourceSettings -> Result wrn ()
108noSettings errorMsg (A.A region settings) =
114 Result.throw region errorMsg
117noPorts :: [A.Commented Effects.PortRaw] -> Result wrn ()
125 toError (A.A (region, _) (Effects.PortRaw name _)) =
126 A.A region (Error.UnexpectedPort name)
128 Result.throwMany (map toError ports)
133 -> Map.Map String [(R.Region, A.Located String)]
134 -> Result wrn Effects.ManagerType
135toManagerType tagRegion settingsDict =
137 toErrors name entries =
138 map (\entry -> A.A (fst entry) (Error.BadSettingOnEffectModule name)) entries
142 |> Map.delete "command"
143 |> Map.delete "subscription"
144 |> Map.mapWithKey toErrors
148 do when (not (null errors)) (Result.throwMany errors)
150 (,) <$> extractOne "command" settingsDict
151 <*> extractOne "subscription" settingsDict
153 -- TODO check that cmd and sub types exist?
155 (Nothing, Nothing) ->
156 Result.throw tagRegion Error.NoSettingsOnEffectModule
158 (Just cmd, Nothing) ->
159 return (Effects.CmdManager cmd)
161 (Nothing, Just sub) ->
162 return (Effects.SubManager sub)
164 (Just cmd, Just sub) ->
165 return (Effects.FxManager cmd sub)
170 -> Map.Map String [(R.Region, A.Located String)]
171 -> Result wrn (Maybe (A.Located String))
172extractOne name settingsDict =
173 case Map.lookup name settingsDict of
178 error "Empty lists should never be added to the dictionary of effect module settings."
180 Just [(_, userType)] ->
181 return (Just userType)
183 Just ((region, _) : _) ->
184 Result.throw region (Error.DuplicateSettingOnEffectModule name)
188-- CHECK EFFECT MANAGER
193 -> Effects.ManagerType
194 -> [A.Commented Valid.Def]
195 -> Result wrn (R.Region, R.Region, R.Region)
196checkManager tagRegion managerType validDefs =
199 Map.fromList (Maybe.mapMaybe getSimpleDefRegion validDefs)
202 <$> requireMaps tagRegion regionDict managerType
203 <*> requireRegion tagRegion regionDict "init"
204 <*> requireRegion tagRegion regionDict "onEffects"
205 <*> requireRegion tagRegion regionDict "onSelfMsg"
208getSimpleDefRegion :: A.Commented Valid.Def -> Maybe (String, R.Region)
209getSimpleDefRegion decl =
211 A.A (region, _) (Valid.Def (A.A _ (Pattern.Var name)) _ _) ->
220 -> Map.Map String R.Region
221 -> Effects.ManagerType
223requireMaps tagRegion regionDict managerType =
226 when (Map.notMember name regionDict) $
227 Result.throw tagRegion (Error.MissingManagerOnEffectModule name)
230 Effects.CmdManager _ ->
233 Effects.SubManager _ ->
236 Effects.FxManager _ _ ->
237 check "cmdMap" <* check "subMap"
240requireRegion :: R.Region -> Map.Map String R.Region -> String -> Result wrn R.Region
241requireRegion tagRegion regionDict name =
242 case Map.lookup name regionDict of
247 Result.throw tagRegion (Error.MissingManagerOnEffectModule name)
254collapseComments :: [D.CommentOr (A.Located a)] -> Result wrn [A.Commented a]
255collapseComments listWithComments =
256 case listWithComments of
260 D.Comment (A.A _ msg) : D.Whatever (A.A region a) : rest ->
263 A.A (region, Just msg) a
265 fmap (entry:) (collapseComments rest)
267 D.Comment (A.A region _) : rest ->
268 collapseComments rest
269 <* Result.throw region Error.CommentOnNothing
272 D.Whatever (A.A region a) : rest ->
275 A.A (region, Nothing) a
277 fmap (entry:) (collapseComments rest)
281-- VALIDATE STRUCTURED SOURCE
284validateDecls :: [D.Source] -> Result wrn ValidStuff
285validateDecls sourceDecls =
286 do rawDecls <- collapseComments sourceDecls
288 validStuff <- validateRawDecls rawDecls
290 let (D.Decls _ unions aliases _) = _structure validStuff
293 <* F.traverse_ checkTypeVarsInUnion unions
294 <* F.traverse_ checkTypeVarsInAlias aliases
295 <* checkDuplicates validStuff
299-- VALIDATE DECLARATIONS
304 { _ports :: [A.Commented Effects.PortRaw]
305 , _structure :: D.Valid
309validateRawDecls :: [A.Commented D.Raw] -> Result wrn ValidStuff
310validateRawDecls commentedDecls =
311 vrdHelp commentedDecls [] (D.Decls [] [] [] [])
315 :: [A.Commented D.Raw]
316 -> [A.Commented Effects.PortRaw]
318 -> Result wrn ValidStuff
319vrdHelp commentedDecls ports structure =
320 case commentedDecls of
322 Result.ok (ValidStuff ports structure)
324 A.A ann decl : rest ->
326 D.Union (D.Type name tvars ctors) ->
327 vrdHelp rest ports (D.addUnion (A.A ann (D.Type name tvars ctors)) structure)
329 D.Alias (D.Type name tvars alias) ->
330 vrdHelp rest ports (D.addAlias (A.A ann (D.Type name tvars alias)) structure)
333 vrdHelp rest ports (D.addInfix fixity structure)
335 D.Def (A.A region def) ->
336 vrdDefHelp rest (A.A (region, snd ann) def) ports structure
339 vrdHelp rest (A.A ann (Effects.PortRaw name tipe) : ports) structure
343 :: [A.Commented D.Raw]
344 -> A.Commented Source.Def'
345 -> [A.Commented Effects.PortRaw]
347 -> Result wrn ValidStuff
348vrdDefHelp remainingDecls (A.A ann def) ports structure =
350 addDef validDef (ValidStuff finalPorts struct) =
351 ValidStuff finalPorts (D.addDef (A.A ann validDef) struct)
354 Source.Definition pat expr ->
356 <$> validateDef pat expr Nothing
357 <*> vrdHelp remainingDecls ports structure
359 Source.Annotation name tipe ->
360 case remainingDecls of
361 A.A _ (D.Def (A.A _ (Source.Definition pat expr))) : rest
362 | Pattern.isVar name pat ->
364 <$> validateDef pat expr (Just tipe)
365 <*> vrdHelp rest ports structure
368 vrdHelp remainingDecls ports structure
369 <* Result.throw (fst ann) (Error.TypeWithoutDefinition name)
373-- VALIDATE DEFINITIONS
376definitions :: [Source.Def] -> Result wrn [Valid.Def]
377definitions sourceDefs =
378 do validDefs <- definitionsHelp sourceDefs
381 |> map Valid.getPattern
382 |> concatMap Pattern.boundVars
383 |> detectDuplicates Error.DuplicateDefinition
388definitionsHelp :: [Source.Def] -> Result wrn [Valid.Def]
389definitionsHelp sourceDefs =
394 A.A _ (Source.Definition pat expr) : rest ->
396 <$> validateDef pat expr Nothing
397 <*> definitionsHelp rest
399 A.A region (Source.Annotation name tipe) : rest ->
401 A.A _ (Source.Definition pat expr) : rest'
402 | Pattern.isVar name pat ->
404 <$> validateDef pat expr (Just tipe)
405 <*> definitionsHelp rest'
408 Result.throw region (Error.TypeWithoutDefinition name)
415 -> Result wrn Valid.Def
416validateDef pat expr maybeType =
417 do validExpr <- expression expr
418 validateDefPattern pat validExpr
419 return $ Valid.Def pat validExpr maybeType
422validateDefPattern :: Pattern.Raw -> Valid.Expr -> Result wrn ()
423validateDefPattern pattern body =
424 case fst (Expr.collectLambdas body) of
430 A.A _ (Pattern.Var funcName) ->
431 checkArguments funcName args
435 (A.A start _) = pattern
436 (A.A end _) = last args
438 Result.throw (R.merge start end) (Error.BadFunctionName (length args))
441checkArguments :: String -> [Pattern.Raw] -> Result wrn ()
442checkArguments funcName args =
445 concatMap Pattern.boundVars args
447 checkDups seenArgs (A.A region arg) =
448 if Set.member arg seenArgs then
449 Result.throw region (Error.DuplicateArgument funcName arg)
452 return (Set.insert arg seenArgs)
454 foldM_ checkDups Set.empty vars
458-- VALIDATE EXPRESSIONS
461expression :: Source.Expr -> Result wrn Valid.Expr
462expression (A.A ann sourceExpression) =
464 case sourceExpression of
468 Lambda pattern body ->
470 <$> validatePattern pattern
473 Binop op leftExpr rightExpr ->
475 <$> expression leftExpr
476 <*> expression rightExpr
481 <*> T.traverse (\(p,b) -> (,) <$> validatePattern p <*> expression b) branches
484 Data name <$> T.traverse expression args
489 Range lowExpr highExpr ->
491 <$> expression lowExpr
492 <*> expression highExpr
494 ExplicitList expressions ->
496 <$> T.traverse expression expressions
498 App funcExpr argExpr ->
500 <$> expression funcExpr
501 <*> expression argExpr
503 If branches finally ->
505 <$> T.traverse both branches
506 <*> expression finally
508 Access record field ->
510 <$> expression record
513 Update record fields ->
515 <$> expression record
516 <*> T.traverse second fields
520 checkDups seenFields (field,_) =
521 if Set.member field seenFields then
522 Result.throw ann (Error.DuplicateFieldName field)
525 return (Set.insert field seenFields)
527 do foldM_ checkDups Set.empty fields
528 Record <$> T.traverse second fields
536 return (Cmd moduleName)
539 return (Sub moduleName)
541 OutgoingPort name tipe ->
542 return (OutgoingPort name tipe)
544 IncomingPort name tipe ->
545 return (IncomingPort name tipe)
548 error "DANGER - Program AST nodes should not be in validation phase."
550 SaveEnv moduleName effects ->
551 return (SaveEnv moduleName effects)
553 GLShader uid src gltipe ->
554 return (GLShader uid src gltipe)
557second :: (a, Source.Expr) -> Result wrn (a, Valid.Expr)
558second (value, expr) =
559 (,) value <$> expression expr
562both :: (Source.Expr, Source.Expr) -> Result wrn (Valid.Expr, Valid.Expr)
564 (,) <$> expression expr1 <*> expression expr2
571validatePattern :: Pattern.Raw -> Result wrn Pattern.Raw
572validatePattern pattern =
573 do detectDuplicates Error.BadPattern (Pattern.boundVars pattern)
581checkDuplicates :: ValidStuff -> Result wrn ()
582checkDuplicates (ValidStuff ports (D.Decls defs unions aliases _)) =
587 concatMap (Pattern.boundVars . Valid.getPattern . A.drop) defs
592 fromPort (A.A (region, _) (Effects.PortRaw name _)) =
597 (types, typeValues) =
598 unzip (map fromUnion unions ++ map fromAlias aliases)
600 fromUnion (A.A (region, _) (D.Type name _ ctors)) =
602 , map (A.A region . fst) ctors
605 fromAlias (A.A (region, _) (D.Type name _ (A.A _ tipe))) =
606 (,) (A.A region name) $
615 [ detectDuplicates Error.DuplicateValueDeclaration (portValues ++ defValues)
616 , detectDuplicates Error.DuplicateValueDeclaration (concat typeValues)
617 , detectDuplicates Error.DuplicateTypeDeclaration types
621detectDuplicates :: (String -> Error.Error) -> [A.Located String] -> Result wrn ()
622detectDuplicates tag names =
624 add (A.A region name) dict =
625 Map.insertWith (++) name [region] dict
628 Map.toList (foldr add Map.empty pairs)
630 check (name, regions) =
633 Result.throw region (tag name)
638 F.traverse_ check (makeGroups names)
642-- UNBOUND TYPE VARIABLES
645checkTypeVarsInUnion :: A.Commented (D.Union Type.Raw) -> Result wrn ()
646checkTypeVarsInUnion (A.A (region,_) (D.Type name boundVars ctors)) =
647 case diff boundVars (concatMap freeVars (concatMap snd ctors)) of
653 (Error.UnboundTypeVarsInUnion name boundVars unbound)
656checkTypeVarsInAlias :: A.Commented (D.Alias Type.Raw) -> Result wrn ()
657checkTypeVarsInAlias (A.A (region,_) (D.Type name boundVars tipe)) =
658 case diff boundVars (freeVars tipe) of
664 (Error.UnboundTypeVarsInAlias name boundVars unbound)
668 (Error.UnusedTypeVarsInAlias name boundVars unused)
672 (Error.MessyTypeVarsInAlias name boundVars unused unbound)
675diff :: [String] -> [A.Located String] -> ([String], [String])
682 Set.fromList (map A.drop right)
684 ( Set.toList (Set.difference leftSet rightSet)
685 , Set.toList (Set.difference rightSet leftSet)
689freeVars :: Type.Raw -> [A.Located String]
690freeVars (A.A region tipe) =
692 Type.RLambda t1 t2 ->
693 freeVars t1 ++ freeVars t2
702 concatMap freeVars (t:ts)
704 Type.RRecord fields ext ->
705 maybe [] freeVars ext
706 ++ concatMap (freeVars . snd) fields
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