1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Nitpick.PatternMatches
13{- The algorithm used here comes from "Warnings for Pattern Matching"
14by Luc Maranget. Check it out for more information!
16http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
20import qualified Data.List as List
21import qualified Data.Map as Map
22import qualified Data.Maybe as Maybe
23import qualified Data.Name as Name
24import qualified Data.Utf8 as Utf8
26import qualified AST.Canonical as Can
27import qualified Data.Index as Index
28import qualified Elm.ModuleName as ModuleName
29import qualified Reporting.Annotation as A
39 | Ctor Can.Union Name.Name [Pattern]
50-- CREATE SIMPLIFIED PATTERNS
53simplify :: Can.Pattern -> Pattern
54simplify (A.At _ pattern) =
68 Can.PTuple a b Nothing ->
69 Ctor pair pairName [ simplify a, simplify b ]
71 Can.PTuple a b (Just c) ->
72 Ctor triple tripleName [ simplify a, simplify b, simplify c ]
74 Can.PCtor _ _ union name _ args ->
76 map (\(Can.PatternCtorArg _ _ arg) -> simplify arg) args
79 foldr cons nil entries
84 Can.PAlias subPattern _ ->
96 Can.PBool union bool ->
97 Ctor union (if bool then Name.true else Name.false) []
100cons :: Can.Pattern -> Pattern -> Pattern
102 Ctor list consName [ simplify hd, tl ]
120 Can.Ctor unitName Index.first 0 []
122 Can.Union [] [ ctor ] 1 Can.Normal
130 Can.Ctor pairName Index.first 2 [Can.TVar "a", Can.TVar "b"]
132 Can.Union ["a","b"] [ ctor ] 1 Can.Normal
135{-# NOINLINE triple #-}
140 Can.Ctor tripleName Index.first 3 [Can.TVar "a", Can.TVar "b", Can.TVar "c"]
142 Can.Union ["a","b","c"] [ ctor ] 1 Can.Normal
150 Can.Ctor nilName Index.first 0 []
153 Can.Ctor consName Index.second 2
155 , Can.TType ModuleName.list Name.list [Can.TVar "a"]
158 Can.Union ["a"] [ nilCtor, consCtor ] 2 Can.Normal
161{-# NOINLINE unitName #-}
166{-# NOINLINE pairName #-}
171{-# NOINLINE tripleName #-}
172tripleName :: Name.Name
176{-# NOINLINE consName #-}
181{-# NOINLINE nilName #-}
191 = Incomplete A.Region Context [Pattern]
192 | Redundant A.Region A.Region Int
205check :: Can.Module -> Either [Error] ()
206check (Can.Module _ _ decls _ _ _ _) =
207 case checkDecls decls [] of
219checkDecls :: Can.Decls -> [Error] -> [Error]
220checkDecls decls errors =
222 Can.Declare def subDecls ->
223 checkDef def $ checkDecls subDecls errors
225 Can.DeclareRec def defs subDecls ->
226 checkDef def (foldr checkDef (checkDecls subDecls errors) defs)
228 Can.SaveTheEnvironment ->
236checkDef :: Can.Def -> [Error] -> [Error]
239 Can.Def _ args body ->
240 foldr checkArg (checkExpr body errors) args
242 Can.TypedDef _ _ args body _ ->
243 foldr checkTypedArg (checkExpr body errors) args
246checkArg :: Can.Pattern -> [Error] -> [Error]
247checkArg pattern@(A.At region _) errors =
248 checkPatterns region BadArg [pattern] errors
251checkTypedArg :: (Can.Pattern, tipe) -> [Error] -> [Error]
252checkTypedArg (pattern@(A.At region _), _) errors =
253 checkPatterns region BadArg [pattern] errors
260checkExpr :: Can.Expr -> [Error] -> [Error]
261checkExpr (A.At region expression) errors =
266 Can.VarTopLevel _ _ ->
272 Can.VarForeign _ _ _ ->
275 Can.VarCtor _ _ _ _ _ ->
278 Can.VarDebug _ _ _ ->
281 Can.VarOperator _ _ _ _ ->
297 foldr checkExpr errors entries
300 checkExpr expr errors
302 Can.Binop _ _ _ _ left right ->
304 checkExpr right errors
306 Can.Lambda args body ->
307 foldr checkArg (checkExpr body errors) args
309 Can.Call func args ->
310 checkExpr func $ foldr checkExpr errors args
312 Can.If branches finally ->
313 foldr checkIfBranch (checkExpr finally errors) branches
316 checkDef def $ checkExpr body errors
318 Can.LetRec defs body ->
319 foldr checkDef (checkExpr body errors) defs
321 Can.LetDestruct pattern@(A.At reg _) expr body ->
322 checkPatterns reg BadDestruct [pattern] $
323 checkExpr expr $ checkExpr body errors
325 Can.Case expr branches ->
326 checkExpr expr $ checkCases region branches errors
331 Can.Access record _ ->
332 checkExpr record errors
334 Can.Update _ record fields ->
335 checkExpr record $ Map.foldr checkField errors fields
338 Map.foldr checkExpr errors fields
343 Can.Tuple a b maybeC ->
361checkField :: Can.FieldUpdate -> [Error] -> [Error]
362checkField (Can.FieldUpdate _ expr) errors =
363 checkExpr expr errors
370checkIfBranch :: (Can.Expr, Can.Expr) -> [Error] -> [Error]
371checkIfBranch (condition, branch) errs =
372 checkExpr condition $ checkExpr branch errs
376-- CHECK CASE EXPRESSION
379checkCases :: A.Region -> [Can.CaseBranch] -> [Error] -> [Error]
380checkCases region branches errors =
382 (patterns, newErrors) =
383 foldr checkCaseBranch ([], errors) branches
385 checkPatterns region BadCase patterns newErrors
388checkCaseBranch :: Can.CaseBranch -> ([Can.Pattern], [Error]) -> ([Can.Pattern], [Error])
389checkCaseBranch (Can.CaseBranch pattern expr) (patterns, errors) =
391 , checkExpr expr errors
399checkPatterns :: A.Region -> Context -> [Can.Pattern] -> [Error] -> [Error]
400checkPatterns region context patterns errors =
401 case toNonRedundantRows region patterns of
406 case isExhaustive matrix 1 of
411 Incomplete region context (map head badPatterns) : errors
415-- EXHAUSTIVE PATTERNS
420-- The initial rows "matrix" are all of length 1
421-- The initial count of items per row "n" is also 1
422-- The resulting rows are examples of missing patterns
424isExhaustive :: [[Pattern]] -> Int -> [[Pattern]]
425isExhaustive matrix n =
428 [replicate n Anything]
435 ctors = collectCtors matrix
436 numSeen = Map.size ctors
440 <$> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
443 let alts@(Can.Union _ altList numAlts _) = snd (Map.findMin ctors) in
444 if numSeen < numAlts then
446 <$> Maybe.mapMaybe (isMissing alts ctors) altList
447 <*> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
451 isAltExhaustive (Can.Ctor name _ arity _) =
452 recoverCtor alts name arity <$>
454 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
457 concatMap isAltExhaustive altList
460isMissing :: Can.Union -> Map.Map Name.Name a -> Can.Ctor -> Maybe Pattern
461isMissing union ctors (Can.Ctor name _ arity _) =
462 if Map.member name ctors then
465 Just (Ctor union name (replicate arity Anything))
468recoverCtor :: Can.Union -> Name.Name -> Int -> [Pattern] -> [Pattern]
469recoverCtor union name arity patterns =
472 splitAt arity patterns
474 Ctor union name args : rest
481-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
482toNonRedundantRows :: A.Region -> [Can.Pattern] -> Either Error [[Pattern]]
483toNonRedundantRows region patterns =
484 toSimplifiedUsefulRows region [] patterns
487-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
488toSimplifiedUsefulRows :: A.Region -> [[Pattern]] -> [Can.Pattern] -> Either Error [[Pattern]]
489toSimplifiedUsefulRows overallRegion checkedRows uncheckedPatterns =
490 case uncheckedPatterns of
494 pattern@(A.At region _) : rest ->
495 let nextRow = [simplify pattern] in
496 if isUseful checkedRows nextRow then
497 toSimplifiedUsefulRows overallRegion (nextRow : checkedRows) rest
499 Left (Redundant overallRegion region (length checkedRows + 1))
502-- Check if a new row "vector" is useful given previous rows "matrix"
503isUseful :: [[Pattern]] -> [Pattern] -> Bool
504isUseful matrix vector =
507 -- No rows are the same as the new vector! The vector is useful!
513 -- There is nothing left in the new vector, but we still have
514 -- rows that match the same things. This is not a useful vector!
517 firstPattern : patterns ->
520 -- keep checking rows that start with this Ctor or Anything
522 (Maybe.mapMaybe (specializeRowByCtor name (length args)) matrix)
526 -- check if all alts appear in matrix
527 case isComplete matrix of
529 -- This Anything is useful because some Ctors are missing.
530 -- But what if a previous row has an Anything?
531 -- If so, this one is not useful.
532 isUseful (Maybe.mapMaybe specializeRowByAnything matrix) patterns
535 -- All Ctors are covered, so this Anything is not needed for any
536 -- of those. But what if some of those Ctors have subpatterns
537 -- that make them less general? If so, this actually is useful!
539 isUsefulAlt (Can.Ctor name _ arity _) =
541 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
542 (replicate arity Anything ++ patterns)
547 -- keep checking rows that start with this Literal or Anything
549 (Maybe.mapMaybe (specializeRowByLiteral literal) matrix)
553-- INVARIANT: (length row == N) ==> (length result == arity + N - 1)
554specializeRowByCtor :: Name.Name -> Int -> [Pattern] -> Maybe [Pattern]
555specializeRowByCtor ctorName arity row =
557 Ctor _ name args : patterns ->
558 if name == ctorName then
559 Just (args ++ patterns)
563 Anything : patterns ->
564 Just (replicate arity Anything ++ patterns)
568 "Compiler bug! After type checking, constructors and literals\
569 \ should never align in pattern match exhaustiveness checks."
572 error "Compiler error! Empty matrices should not get specialized."
575-- INVARIANT: (length row == N) ==> (length result == N-1)
576specializeRowByLiteral :: Literal -> [Pattern] -> Maybe [Pattern]
577specializeRowByLiteral literal row =
579 Literal lit : patterns ->
580 if lit == literal then
585 Anything : patterns ->
590 "Compiler bug! After type checking, constructors and literals\
591 \ should never align in pattern match exhaustiveness checks."
594 error "Compiler error! Empty matrices should not get specialized."
597-- INVARIANT: (length row == N) ==> (length result == N-1)
598specializeRowByAnything :: [Pattern] -> Maybe [Pattern]
599specializeRowByAnything row =
607 Anything : patterns ->
615-- ALL CONSTRUCTORS ARE PRESENT?
623isComplete :: [[Pattern]] -> Complete
626 ctors = collectCtors matrix
627 numSeen = Map.size ctors
632 let (Can.Union _ alts numAlts _) = snd (Map.findMin ctors) in
633 if numSeen == numAlts then Yes alts else No
640collectCtors :: [[Pattern]] -> Map.Map Name.Name Can.Union
642 List.foldl' collectCtorsHelp Map.empty matrix
645collectCtorsHelp :: Map.Map Name.Name Can.Union -> [Pattern] -> Map.Map Name.Name Can.Union
646collectCtorsHelp ctors row =
648 Ctor union name _ : _ ->
649 Map.insert name union ctors
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