1{-# OPTIONS_GHC -Wall #-}
2module Nitpick.PatternMatches (patternMatches) where
4{- The algorithm used here comes from "Warnings for Pattern Matching"
5by Luc Maranget. Check it out for more information!
7http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
11import Control.Arrow ((***), second)
12import qualified Data.Foldable as F
13import qualified Data.List as List
14import qualified Data.Map as Map
15import qualified Data.Maybe as Maybe
16import qualified Data.Set as Set
18import qualified AST.Expression.Canonical as C
19import qualified AST.Helpers as Help
20import qualified AST.Literal as L
21import qualified AST.Module as Module
22import qualified AST.Module.Name as ModuleName
23import qualified AST.Pattern as Pattern
24import qualified AST.Variable as Var
25import Elm.Utils ((|>))
26import Nitpick.Pattern (Pattern(..), fromCanonicalPattern)
27import qualified Optimize.DecisionTree as DT
28import qualified Reporting.Annotation as A
29import qualified Reporting.Error.Pattern as Error
30import qualified Reporting.Region as Region
31import qualified Reporting.Result as Result
35-- NITPICK PATTERN MATCHES
38type Result warning a =
39 Result.Result () warning Error.Error a
42patternMatches :: Module.Interfaces -> Module.Canonical -> Result wrn DT.VariantDict
43patternMatches interfaces (Module.Module name _ info) =
46 toArityDict interfaces name (Module.unions info)
49 Map.map (Map.map _size) arityDict
52 <* checkExpression arityDict (Module.program info)
55checkExpression :: ArityDict -> C.Expr -> Result wrn ()
56checkExpression arityDict (A.A region expression) =
59 checkExpression arityDict
72 F.traverse_ go listExprs
74 C.Binop _ leftExpr rightExpr ->
75 go2 leftExpr rightExpr
77 C.Lambda pattern@(A.A patRegion _) body ->
78 checkPatterns arityDict patRegion Error.Arg [pattern]
84 C.If branches finally ->
85 F.traverse_ (uncurry go2) branches
90 <* F.traverse_ goDef defs
92 goDef (C.Def _ pattern@(A.A patRegion _) expr _) =
93 checkPatterns arityDict patRegion Error.LetBound [pattern]
96 C.Case expr branches ->
98 <* checkPatterns arityDict region Error.Case (map fst branches)
99 <* F.traverse_ (go . snd) branches
101 C.Ctor _ctor exprs ->
104 C.Access record _field ->
107 C.Update record fields ->
109 <* F.traverse_ (go . snd) fields
112 F.traverse_ (go . snd) fields
120 C.OutgoingPort _ _ ->
123 C.IncomingPort _ _ ->
127 error "DANGER - Program AST nodes should not be in Nitpick.PatternMatches."
140checkPatterns :: ArityDict -> Region.Region -> Error.Origin -> [Pattern.Canonical] -> Result wrn ()
141checkPatterns arityDict region origin patterns =
142 do matrix <- checkRedundant arityDict region [] patterns
143 if isUseful arityDict matrix [Anything]
144 then Result.throw region (Error.Incomplete origin (error "TODO"))
152 -> [Pattern.Canonical]
153 -> Result wrn [[Pattern]]
154checkRedundant arityDict region checked unchecked =
159 nextPattern@(A.A patRegion _) : rest ->
162 [fromCanonicalPattern nextPattern]
164 if isUseful arityDict checked next then
165 checkRedundant arityDict region (next : checked) rest
167 Result.throw region (Error.Redundant patRegion (length checked + 1))
174--missingPatterns :: [[Pattern]] -> Int -> [Pattern]
175--missingPatterns matrix n =
182-- DETECT USEFUL PATTERNS
185isUseful :: ArityDict -> [[Pattern]] -> [Pattern] -> Bool
186isUseful arityDict matrix vector =
187 case (matrix, vector) of
194 (_, firstPattern : patterns) ->
199 Maybe.mapMaybe (specialize name (length args)) matrix
201 isUseful arityDict newMatrix (args ++ patterns)
204 case isComplete arityDict matrix of
206 isUseful arityDict (Maybe.mapMaybe toDefault matrix) patterns
210 isUsefulHelp (ctor, arity) =
213 (Maybe.mapMaybe (specialize ctor arity) matrix)
214 (replicate arity Anything ++ patterns)
216 any isUsefulHelp arityInfo
221 Maybe.mapMaybe (specializeLiteral literal) matrix
223 isUseful arityDict newMatrix patterns
227-- SPECIALIZE MATRICES
230specialize :: Var.Canonical -> Int -> [Pattern] -> Maybe [Pattern]
231specialize ctorName arity row =
234 error "Compiler error! Empty matrices should not get specialized."
236 firstPattern : patterns ->
239 if name == ctorName then Just (args ++ patterns) else Nothing
242 Just (replicate arity Anything ++ patterns)
246 "Compiler bug! After type checking, constructors and literals\
247 \ should never align in pattern match exhaustiveness checks."
250specializeLiteral :: L.Literal -> [Pattern] -> Maybe [Pattern]
251specializeLiteral literal row =
254 error "Compiler error! Empty matrices should not get specialized."
256 firstPattern : patterns ->
259 if lit == literal then Just patterns else Nothing
266 "Compiler bug! After type checking, constructors and literals\
267 \ should never align in pattern match exhaustiveness checks."
270toDefault :: [Pattern] -> Maybe [Pattern]
279 Anything : patterns ->
287-- ALL CONSTRUCTORS ARE PRESENT?
290isComplete :: ArityDict -> [[Pattern]] -> Maybe [(Var.Canonical, Int)]
291isComplete arityDict matrix =
294 List.foldl' isCompleteHelp Set.empty matrix
304 (ArityInfo expected info) =
305 getArityInfo (Set.findMin ctorSet) arityDict
307 if expected == actual then Just info else Nothing
310isCompleteHelp :: Set.Set Var.Canonical -> [Pattern] -> Set.Set Var.Canonical
311isCompleteHelp ctors row =
314 Set.insert name ctors
325 Map.Map Var.Home (Map.Map String ArityInfo)
331 , _info :: [(Var.Canonical, Int)]
335toArityDict :: Module.Interfaces -> ModuleName.Canonical -> Module.Unions -> ArityDict
336toArityDict interfaces localName localUnions =
338 |> Map.mapWithKey (\name iface -> toArityEntry name (Module.iUnions iface))
339 |> Map.mapKeysMonotonic Var.Module
340 |> Map.insert (Var.Module localName) (toArityEntry localName localUnions)
341 |> Map.union builtinDict
344builtinDict :: ArityDict
348 ArityInfo 2 [ (Var.builtin "::", 2), (Var.builtin "[]", 0) ]
351 ArityInfo 2 [ (Var.builtin "True", 0), (Var.builtin "False", 0) ]
353 Map.singleton Var.BuiltIn $ Map.fromList $
357 , ("False", boolInfo)
359 ++ map makeTupleInfo [0..8]
362makeTupleInfo :: Int -> ( String, ArityInfo )
368 ( name, ArityInfo 1 [ (Var.builtin name, n) ] )
371toArityEntry :: ModuleName.Canonical -> Module.Unions -> Map.Map String ArityInfo
372toArityEntry name unions =
373 Map.fromList (concatMap (toArityEntryHelp name) (Map.elems unions))
376toArityEntryHelp :: ModuleName.Canonical -> Module.UnionInfo String -> [(String, ArityInfo)]
377toArityEntryHelp name (_tvars, ctors) =
380 ArityInfo (length ctors) (map (Var.fromModule name *** length) ctors)
382 map (second (\_ -> arityInfo)) ctors
385getArityInfo :: Var.Canonical -> ArityDict -> ArityInfo
386getArityInfo var@(Var.Canonical home name) arityDict =
387 case Map.lookup name =<< Map.lookup home arityDict of
392 if Help.isTuple name then
393 ArityInfo 1 [ (var, read (drop 6 name)) ]
396 "Since the Nitpick phase happens after canonicalization and type\
397 \ inference, it is impossible that a pattern in a case cannot be\
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