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 vrdHelp rest (A.A ann (Effects.PortRaw name tipe) : ports) structure
340 :: [A.Commented D.Raw]
341 -> A.Commented Src.RawDef'
342 -> [A.Commented Effects.PortRaw]
344 -> Result wrn ValidStuff
345vrdDefHelp remainingDecls (A.A ann def) ports structure =
347 addDef validDef (ValidStuff finalPorts struct) =
348 ValidStuff finalPorts (D.addDef (A.A ann validDef) struct)
351 Src.Definition pat expr ->
353 <$> validateDef (fst ann) pat expr Nothing
354 <*> vrdHelp remainingDecls ports structure
356 Src.Annotation name tipe ->
357 case remainingDecls of
358 A.A _ (D.Def (A.A defRegion (Src.Definition pat expr))) : rest
359 | Pattern.isVar name pat ->
361 <$> validateDef (R.merge (fst ann) defRegion) pat expr (Just tipe)
362 <*> vrdHelp rest ports structure
365 vrdHelp remainingDecls ports structure
366 <* Result.throw (fst ann) (Error.TypeWithoutDefinition name)
370-- VALIDATE DEFINITIONS
373definitions :: [Src.RawDef] -> Result wrn [Src.ValidDef]
374definitions sourceDefs =
375 do validDefs <- definitionsHelp sourceDefs
378 |> map Src.getPattern
379 |> concatMap Pattern.boundVars
380 |> detectDuplicates Error.DuplicateDefinition
385definitionsHelp :: [Src.RawDef] -> Result wrn [Src.ValidDef]
386definitionsHelp sourceDefs =
391 A.A defRegion (Src.Definition pat expr) : rest ->
393 <$> validateDef defRegion pat expr Nothing
394 <*> definitionsHelp rest
396 A.A annRegion (Src.Annotation name tipe) : rest ->
398 A.A defRegion (Src.Definition pat expr) : rest'
399 | Pattern.isVar name pat ->
401 <$> validateDef (R.merge annRegion defRegion) pat expr (Just tipe)
402 <*> definitionsHelp rest'
405 Result.throw annRegion (Error.TypeWithoutDefinition name)
413 -> Result wrn Src.ValidDef
414validateDef region pat expr maybeType =
415 do validExpr <- expression expr
416 validateDefPattern pat validExpr
417 return $ Src.Def region pat validExpr maybeType
420validateDefPattern :: Pattern.Raw -> Src.ValidExpr -> Result wrn ()
421validateDefPattern pattern body =
422 case fst (Src.collectLambdas body) of
428 A.A _ (Pattern.Var funcName) ->
429 checkArguments funcName args
433 (A.A start _) = pattern
434 (A.A end _) = last args
436 Result.throw (R.merge start end) (Error.BadFunctionName (length args))
439checkArguments :: String -> [Pattern.Raw] -> Result wrn ()
440checkArguments funcName args =
443 concatMap Pattern.boundVars args
445 checkDups seenArgs (A.A region arg) =
446 if Set.member arg seenArgs then
447 Result.throw region (Error.DuplicateArgument funcName arg)
450 return (Set.insert arg seenArgs)
452 foldM_ checkDups Set.empty vars
456-- VALIDATE EXPRESSIONS
459expression :: Src.RawExpr -> Result wrn Src.ValidExpr
460expression (A.A ann sourceExpression) =
462 case sourceExpression of
466 Src.Lambda pattern body ->
468 <$> validatePattern pattern
471 Src.Binop ops lastExpr ->
473 <$> traverse (\(expr,op) -> (,) <$> expression expr <*> pure op) ops
474 <*> expression lastExpr
476 Src.Case e branches ->
479 <*> traverse (\(p,b) -> (,) <$> validatePattern p <*> expression b) branches
481 Src.Ctor name args ->
482 Src.Ctor name <$> traverse expression args
485 return (Src.Literal lit)
487 Src.List expressions ->
489 <$> traverse expression expressions
491 Src.App funcExpr argExpr ->
493 <$> expression funcExpr
494 <*> expression argExpr
496 Src.If branches finally ->
498 <$> traverse both branches
499 <*> expression finally
501 Src.Access record field ->
503 <$> expression record
506 Src.Update record fields ->
508 <$> expression record
509 <*> traverse second fields
513 checkDups seenFields (field,_) =
514 if Set.member field seenFields then
515 Result.throw ann (Error.DuplicateFieldName field)
518 return (Set.insert field seenFields)
520 do foldM_ checkDups Set.empty fields
521 Src.Record <$> traverse second fields
528 Src.GLShader uid src gltipe ->
529 return (Src.GLShader uid src gltipe)
532second :: (a, Src.RawExpr) -> Result wrn (a, Src.ValidExpr)
533second (value, expr) =
534 (,) value <$> expression expr
537both :: (Src.RawExpr, Src.RawExpr) -> Result wrn (Src.ValidExpr, Src.ValidExpr)
539 (,) <$> expression expr1 <*> expression expr2
546validatePattern :: Pattern.Raw -> Result wrn Pattern.Raw
547validatePattern pattern =
548 do detectDuplicates Error.BadPattern (Pattern.boundVars pattern)
556checkDuplicates :: ValidStuff -> Result wrn ()
557checkDuplicates (ValidStuff ports (D.Decls defs unions aliases _)) =
562 concatMap (Pattern.boundVars . Src.getPattern . A.drop) defs
567 fromPort (A.A (region, _) (Effects.PortRaw name _)) =
572 (types, typeValues) =
573 unzip (map fromUnion unions ++ map fromAlias aliases)
575 fromUnion (A.A (region, _) (D.Type name _ ctors)) =
577 , map (A.A region . fst) ctors
580 fromAlias (A.A (region, _) (D.Type name _ (A.A _ tipe))) =
581 (,) (A.A region name) $
590 [ detectDuplicates Error.DuplicateValueDeclaration (portValues ++ defValues)
591 , detectDuplicates Error.DuplicateValueDeclaration (concat typeValues)
592 , detectDuplicates Error.DuplicateTypeDeclaration types
596detectDuplicates :: (String -> Error.Error) -> [A.Located String] -> Result wrn ()
597detectDuplicates tag names =
599 add (A.A region name) dict =
600 Map.insertWith (++) name [region] dict
603 Map.toList (foldr add Map.empty pairs)
605 check (name, regions) =
608 Result.throw region (tag name)
613 F.traverse_ check (makeGroups names)
617-- UNBOUND TYPE VARIABLES
620checkTypeVarsInUnion :: A.Commented (D.Union Type.Raw) -> Result wrn ()
621checkTypeVarsInUnion (A.A (region,_) (D.Type name boundVars ctors)) =
622 case diff boundVars (concatMap freeVars (concatMap snd ctors)) of
628 (Error.UnboundTypeVarsInUnion name boundVars unbound)
631checkTypeVarsInAlias :: A.Commented (D.Alias Type.Raw) -> Result wrn ()
632checkTypeVarsInAlias (A.A (region,_) (D.Type name boundVars tipe)) =
633 case diff boundVars (freeVars tipe) of
639 (Error.UnboundTypeVarsInAlias name boundVars unbound)
643 (Error.UnusedTypeVarsInAlias name boundVars unused)
647 (Error.MessyTypeVarsInAlias name boundVars unused unbound)
650diff :: [String] -> [A.Located String] -> ([String], [String])
657 Set.fromList (map A.drop right)
659 ( Set.toList (Set.difference leftSet rightSet)
660 , Set.toList (Set.difference rightSet leftSet)
664freeVars :: Type.Raw -> [A.Located String]
665freeVars (A.A region tipe) =
667 Type.RLambda t1 t2 ->
668 freeVars t1 ++ freeVars t2
677 concatMap freeVars (t:ts)
679 Type.RRecord fields ext ->
680 maybe [] freeVars ext
681 ++ 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