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
10import qualified AST.Effects as Effects
11import qualified AST.Expression.Source as Src
12import qualified AST.Declaration as D
13import qualified AST.Module as Module
14import qualified AST.Module.Name as ModuleName
15import qualified AST.Pattern as Pattern
16import qualified AST.Type as Type
17import qualified Elm.Compiler.Imports as Imports
18import qualified Elm.Package as Package
19import Elm.Utils ((|>))
20import qualified Reporting.Annotation as A
21import qualified Reporting.Error.Syntax as Error
22import qualified Reporting.Region as R
23import qualified Reporting.Result as Result
27type Result warning a =
28 Result.Result () warning Error.Error a
35module' :: Module.Source -> Result wrn Module.Valid
36module' (Module.Module name path info) =
38 (ModuleName.Canonical pkgName _) =
41 (Module.Source tag settings docs exports imports decls) =
44 do (ValidStuff ports structure) <- validateDecls decls
46 validEffects <- validateEffects tag settings ports (D._defs structure)
48 return $ Module.Module name path $
49 Module.Valid docs exports (addDefaults pkgName imports) structure validEffects
58 -> [Module.UserImport]
59 -> ([Module.DefaultImport], [Module.UserImport])
60addDefaults pkgName imports =
62 if pkgName == Package.core then
75 -> Module.SourceSettings
76 -> [A.Commented Effects.PortRaw]
77 -> [A.Commented Src.ValidDef]
78 -> Result wrn Effects.Raw
79validateEffects tag settings@(A.A _ pairs) ports validDefs =
82 do noSettings Error.SettingsOnNormalModule settings
87 do noSettings Error.SettingsOnPortModule settings
88 return (Effects.Port ports)
90 Module.Effect tagRegion ->
92 collectSettings (A.A region setting, userValue) dict =
93 Map.insertWith (++) setting [(region, userValue)] dict
96 foldr collectSettings Map.empty pairs
99 managerType <- toManagerType tagRegion settingsDict
100 (r0, r1, r2) <- checkManager tagRegion managerType validDefs
101 return (Effects.Manager () (Effects.Info tagRegion r0 r1 r2 managerType))
104noSettings :: Error.Error -> Module.SourceSettings -> Result wrn ()
105noSettings errorMsg (A.A region settings) =
111 Result.throw region errorMsg
114noPorts :: [A.Commented Effects.PortRaw] -> Result wrn ()
122 toError (A.A (region, _) (Effects.PortRaw name _)) =
123 A.A region (Error.UnexpectedPort name)
125 Result.throwMany (map toError ports)
130 -> Map.Map String [(R.Region, A.Located String)]
131 -> Result wrn Effects.ManagerType
132toManagerType tagRegion settingsDict =
134 toErrors name entries =
135 map (\entry -> A.A (fst entry) (Error.BadSettingOnEffectModule name)) entries
139 |> Map.delete "command"
140 |> Map.delete "subscription"
141 |> Map.mapWithKey toErrors
145 do when (not (null errors)) (Result.throwMany errors)
147 (,) <$> extractOne "command" settingsDict
148 <*> extractOne "subscription" settingsDict
150 -- TODO check that cmd and sub types exist?
152 (Nothing, Nothing) ->
153 Result.throw tagRegion Error.NoSettingsOnEffectModule
155 (Just cmd, Nothing) ->
156 return (Effects.CmdManager cmd)
158 (Nothing, Just sub) ->
159 return (Effects.SubManager sub)
161 (Just cmd, Just sub) ->
162 return (Effects.FxManager cmd sub)
167 -> Map.Map String [(R.Region, A.Located String)]
168 -> Result wrn (Maybe (A.Located String))
169extractOne name settingsDict =
170 case Map.lookup name settingsDict of
175 error "Empty lists should never be added to the dictionary of effect module settings."
177 Just [(_, userType)] ->
178 return (Just userType)
180 Just ((region, _) : _) ->
181 Result.throw region (Error.DuplicateSettingOnEffectModule name)
185-- CHECK EFFECT MANAGER
190 -> Effects.ManagerType
191 -> [A.Commented Src.ValidDef]
192 -> Result wrn (R.Region, R.Region, R.Region)
193checkManager tagRegion managerType validDefs =
196 Map.fromList (Maybe.mapMaybe getSimpleDefRegion validDefs)
199 <$> requireMaps tagRegion regionDict managerType
200 <*> requireRegion tagRegion regionDict "init"
201 <*> requireRegion tagRegion regionDict "onEffects"
202 <*> requireRegion tagRegion regionDict "onSelfMsg"
205getSimpleDefRegion :: A.Commented Src.ValidDef -> Maybe (String, R.Region)
206getSimpleDefRegion decl =
208 A.A _ (Src.Def region (A.A _ (Pattern.Var name)) _ _) ->
217 -> Map.Map String R.Region
218 -> Effects.ManagerType
220requireMaps tagRegion regionDict managerType =
223 when (Map.notMember name regionDict) $
224 Result.throw tagRegion (Error.MissingManagerOnEffectModule name)
227 Effects.CmdManager _ ->
230 Effects.SubManager _ ->
233 Effects.FxManager _ _ ->
234 check "cmdMap" <* check "subMap"
237requireRegion :: R.Region -> Map.Map String R.Region -> String -> Result wrn R.Region
238requireRegion tagRegion regionDict name =
239 case Map.lookup name regionDict of
244 Result.throw tagRegion (Error.MissingManagerOnEffectModule name)
251collapseComments :: [D.CommentOr (A.Located a)] -> Result wrn [A.Commented a]
252collapseComments listWithComments =
253 case listWithComments of
257 D.Comment (A.A _ msg) : D.Whatever (A.A region a) : rest ->
260 A.A (region, Just msg) a
262 fmap (entry:) (collapseComments rest)
264 D.Comment (A.A region _) : rest ->
265 collapseComments rest
266 <* Result.throw region Error.CommentOnNothing
269 D.Whatever (A.A region a) : rest ->
272 A.A (region, Nothing) a
274 fmap (entry:) (collapseComments rest)
278-- VALIDATE STRUCTURED SOURCE
281validateDecls :: [D.Source] -> Result wrn ValidStuff
282validateDecls sourceDecls =
283 do rawDecls <- collapseComments sourceDecls
285 validStuff <- validateRawDecls rawDecls
287 let (D.Decls _ unions aliases _) = _structure validStuff
290 <* F.traverse_ checkTypeVarsInUnion unions
291 <* F.traverse_ checkTypeVarsInAlias aliases
292 <* checkDuplicates validStuff
296-- VALIDATE DECLARATIONS
301 { _ports :: [A.Commented Effects.PortRaw]
302 , _structure :: D.Valid
306validateRawDecls :: [A.Commented D.Raw] -> Result wrn ValidStuff
307validateRawDecls commentedDecls =
308 vrdHelp commentedDecls [] (D.Decls [] [] [] [])
312 :: [A.Commented D.Raw]
313 -> [A.Commented Effects.PortRaw]
315 -> Result wrn ValidStuff
316vrdHelp commentedDecls ports structure =
317 case commentedDecls of
319 Result.ok (ValidStuff ports structure)
321 A.A ann decl : rest ->
323 D.Union (D.Type name tvars ctors) ->
324 vrdHelp rest ports (D.addUnion (A.A ann (D.Type name tvars ctors)) structure)
326 D.Alias (D.Type name tvars alias) ->
327 vrdHelp rest ports (D.addAlias (A.A ann (D.Type name tvars alias)) structure)
330 vrdHelp rest ports (D.addInfix fixity structure)
332 D.Def (A.A region def) ->
333 vrdDefHelp rest (A.A (region, snd ann) def) ports structure
336 do checkDuplicateFieldsInType tipe
337 vrdHelp rest (A.A ann (Effects.PortRaw name tipe) : ports) structure
341 :: [A.Commented D.Raw]
342 -> A.Commented Src.RawDef'
343 -> [A.Commented Effects.PortRaw]
345 -> Result wrn ValidStuff
346vrdDefHelp remainingDecls (A.A ann def) ports structure =
348 addDef validDef (ValidStuff finalPorts struct) =
349 ValidStuff finalPorts (D.addDef (A.A ann validDef) struct)
352 Src.Definition pat expr ->
354 <$> validateDef (fst ann) pat expr Nothing
355 <*> vrdHelp remainingDecls ports structure
357 Src.Annotation name tipe ->
358 case remainingDecls of
359 A.A _ (D.Def (A.A defRegion (Src.Definition pat expr))) : rest
360 | Pattern.isVar name pat ->
362 <$> validateDef (R.merge (fst ann) defRegion) pat expr (Just tipe)
363 <*> vrdHelp rest ports structure
366 vrdHelp remainingDecls ports structure
367 <* Result.throw (fst ann) (Error.TypeWithoutDefinition name)
371-- VALIDATE DEFINITIONS
374definitions :: [Src.RawDef] -> Result wrn [Src.ValidDef]
375definitions sourceDefs =
376 do validDefs <- definitionsHelp sourceDefs
379 |> map Src.getPattern
380 |> concatMap Pattern.boundVars
381 |> detectDuplicates Error.DuplicateDefinition
386definitionsHelp :: [Src.RawDef] -> Result wrn [Src.ValidDef]
387definitionsHelp sourceDefs =
392 A.A defRegion (Src.Definition pat expr) : rest ->
394 <$> validateDef defRegion pat expr Nothing
395 <*> definitionsHelp rest
397 A.A annRegion (Src.Annotation name tipe) : rest ->
399 A.A defRegion (Src.Definition pat expr) : rest'
400 | Pattern.isVar name pat ->
402 <$> validateDef (R.merge annRegion defRegion) pat expr (Just tipe)
403 <*> definitionsHelp rest'
406 Result.throw annRegion (Error.TypeWithoutDefinition name)
414 -> Result wrn Src.ValidDef
415validateDef region pat expr maybeType =
416 do validExpr <- expression expr
417 validateDefPattern pat validExpr
418 F.traverse_ checkDuplicateFieldsInType maybeType
419 return $ Src.Def region pat validExpr maybeType
422validateDefPattern :: Pattern.Raw -> Src.ValidExpr -> Result wrn ()
423validateDefPattern pattern body =
424 case fst (Src.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 :: Src.RawExpr -> Result wrn Src.ValidExpr
462expression (A.A ann sourceExpression) =
464 case sourceExpression of
468 Src.Lambda pattern body ->
470 <$> validatePattern pattern
473 Src.Binop ops lastExpr ->
475 <$> traverse (\(expr,op) -> (,) <$> expression expr <*> pure op) ops
476 <*> expression lastExpr
478 Src.Case e branches ->
481 <*> traverse (\(p,b) -> (,) <$> validatePattern p <*> expression b) branches
483 Src.Ctor name args ->
484 Src.Ctor name <$> traverse expression args
487 return (Src.Literal lit)
489 Src.List expressions ->
491 <$> traverse expression expressions
493 Src.App funcExpr argExpr ->
495 <$> expression funcExpr
496 <*> expression argExpr
498 Src.If branches finally ->
500 <$> traverse both branches
501 <*> expression finally
503 Src.Access record field ->
505 <$> expression record
508 Src.Update record fields ->
509 do checkDuplicateFields ann fields
510 Src.Update <$> expression record <*> traverse second fields
513 do checkDuplicateFields ann fields
514 Src.Record <$> traverse second fields
521 Src.GLShader uid src gltipe ->
522 return (Src.GLShader uid src gltipe)
525second :: (a, Src.RawExpr) -> Result wrn (a, Src.ValidExpr)
526second (value, expr) =
527 (,) value <$> expression expr
530both :: (Src.RawExpr, Src.RawExpr) -> Result wrn (Src.ValidExpr, Src.ValidExpr)
532 (,) <$> expression expr1 <*> expression expr2
535checkDuplicateFields :: R.Region -> [(A.Located String, a)] -> Result wrn ()
536checkDuplicateFields recordRegion fields =
538 checkDups seenFields ( A.A region field, _ ) =
539 if Set.member field seenFields then
540 Result.throw recordRegion (Error.DuplicateFieldName region field)
542 Result.ok (Set.insert field seenFields)
544 foldM_ checkDups Set.empty fields
551validatePattern :: Pattern.Raw -> Result wrn Pattern.Raw
552validatePattern pattern =
553 do detectDuplicates Error.BadPattern (Pattern.boundVars pattern)
561checkDuplicates :: ValidStuff -> Result wrn ()
562checkDuplicates (ValidStuff ports (D.Decls defs unions aliases _)) =
567 concatMap (Pattern.boundVars . Src.getPattern . A.drop) defs
572 fromPort (A.A (region, _) (Effects.PortRaw name _)) =
577 (types, typeValues) =
578 unzip (map fromUnion unions ++ map fromAlias aliases)
580 fromUnion (A.A (region, _) (D.Type name _ ctors)) =
582 , map (A.A region . fst) ctors
585 fromAlias (A.A (region, _) (D.Type name _ (A.A _ tipe))) =
586 (,) (A.A region name) $
595 [ detectDuplicates Error.DuplicateValueDeclaration (portValues ++ defValues)
596 , detectDuplicates Error.DuplicateValueDeclaration (concat typeValues)
597 , detectDuplicates Error.DuplicateTypeDeclaration types
601detectDuplicates :: (String -> Error.Error) -> [A.Located String] -> Result wrn ()
602detectDuplicates tag names =
604 add (A.A region name) dict =
605 Map.insertWith (++) name [region] dict
608 Map.toList (foldr add Map.empty pairs)
610 check (name, regions) =
613 Result.throw region (tag name)
618 F.traverse_ check (makeGroups names)
622-- UNBOUND TYPE VARIABLES
625checkTypeVarsInUnion :: A.Commented (D.Union Type.Raw) -> Result wrn ()
626checkTypeVarsInUnion (A.A (region,_) (D.Type name boundVars ctors)) =
627 do allFreeVars <- concat <$> traverse freeVars (concatMap snd ctors)
628 case diff boundVars allFreeVars of
634 (Error.UnboundTypeVarsInUnion name boundVars unbound)
637checkTypeVarsInAlias :: A.Commented (D.Alias Type.Raw) -> Result wrn ()
638checkTypeVarsInAlias (A.A (region,_) (D.Type name boundVars tipe)) =
639 do allFreeVars <- freeVars tipe
640 case diff boundVars allFreeVars of
646 (Error.UnboundTypeVarsInAlias name boundVars unbound)
650 (Error.UnusedTypeVarsInAlias name boundVars unused)
654 (Error.MessyTypeVarsInAlias name boundVars unused unbound)
657diff :: [String] -> [A.Located String] -> ([String], [String])
664 Set.fromList (map A.drop right)
666 ( Set.toList (Set.difference leftSet rightSet)
667 , Set.toList (Set.difference rightSet leftSet)
671freeVars :: Type.Raw -> Result wrn [A.Located String]
672freeVars (A.A region tipe) =
674 Type.RLambda t1 t2 ->
675 (++) <$> freeVars t1 <*> freeVars t2
678 Result.ok [A.A region x]
684 concat <$> traverse freeVars (t:ts)
686 Type.RRecord fields ext ->
687 do checkDuplicateFields region fields
688 let types = Maybe.maybeToList ext ++ map snd fields
689 concat <$> traverse freeVars types
692checkDuplicateFieldsInType :: Type.Raw -> Result wrn ()
693checkDuplicateFieldsInType (A.A region tipe) =
695 Type.RLambda t1 t2 ->
696 checkDuplicateFieldsInType t1
697 <* checkDuplicateFieldsInType t2
706 F.traverse_ checkDuplicateFieldsInType (t:ts)
708 Type.RRecord fields ext ->
709 do checkDuplicateFields region fields
710 let types = Maybe.maybeToList ext ++ map snd fields
711 F.traverse_ checkDuplicateFieldsInType types
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