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.NonEmptyList as NE
26import qualified AST.Canonical as Can
27import qualified Data.Index as Index
28import qualified Elm.ModuleName as ModuleName
29import qualified Elm.String as ES
30import qualified Reporting.Annotation as A
40 | Ctor Can.Union Name.Name [Pattern]
51-- CREATE SIMPLIFIED PATTERNS
54simplify :: Can.Pattern -> Pattern
55simplify (A.At _ pattern) =
69 Can.PTuple a b Nothing ->
70 Ctor pair pairName [ simplify a, simplify b ]
72 Can.PTuple a b (Just c) ->
73 Ctor triple tripleName [ simplify a, simplify b, simplify c ]
75 Can.PCtor _ _ union name _ args ->
77 map (\(Can.PatternCtorArg _ _ arg) -> simplify arg) args
80 foldr cons nil entries
85 Can.PAlias subPattern _ ->
97 Can.PBool union bool ->
98 Ctor union (if bool then Name.true else Name.false) []
101cons :: Can.Pattern -> Pattern -> Pattern
103 Ctor list consName [ simplify hd, tl ]
121 Can.Ctor unitName Index.first 0 []
123 Can.Union [] [ ctor ] 1 Can.Normal
131 Can.Ctor pairName Index.first 2 [Can.TVar "a", Can.TVar "b"]
133 Can.Union ["a","b"] [ ctor ] 1 Can.Normal
136{-# NOINLINE triple #-}
141 Can.Ctor tripleName Index.first 3 [Can.TVar "a", Can.TVar "b", Can.TVar "c"]
143 Can.Union ["a","b","c"] [ ctor ] 1 Can.Normal
151 Can.Ctor nilName Index.first 0 []
154 Can.Ctor consName Index.second 2
156 , Can.TType ModuleName.list Name.list [Can.TVar "a"]
159 Can.Union ["a"] [ nilCtor, consCtor ] 2 Can.Normal
162{-# NOINLINE unitName #-}
167{-# NOINLINE pairName #-}
172{-# NOINLINE tripleName #-}
173tripleName :: Name.Name
177{-# NOINLINE consName #-}
182{-# NOINLINE nilName #-}
192 = Incomplete A.Region Context [Pattern]
193 | Redundant A.Region A.Region Int
206check :: Can.Module -> Either (NE.List Error) ()
207check (Can.Module _ _ _ decls _ _ _ _) =
208 case checkDecls decls [] of
220checkDecls :: Can.Decls -> [Error] -> [Error]
221checkDecls decls errors =
223 Can.Declare def subDecls ->
224 checkDef def $ checkDecls subDecls errors
226 Can.DeclareRec def defs subDecls ->
227 checkDef def (foldr checkDef (checkDecls subDecls errors) defs)
229 Can.SaveTheEnvironment ->
237checkDef :: Can.Def -> [Error] -> [Error]
240 Can.Def _ args body ->
241 foldr checkArg (checkExpr body errors) args
243 Can.TypedDef _ _ args body _ ->
244 foldr checkTypedArg (checkExpr body errors) args
247checkArg :: Can.Pattern -> [Error] -> [Error]
248checkArg pattern@(A.At region _) errors =
249 checkPatterns region BadArg [pattern] errors
252checkTypedArg :: (Can.Pattern, tipe) -> [Error] -> [Error]
253checkTypedArg (pattern@(A.At region _), _) errors =
254 checkPatterns region BadArg [pattern] errors
261checkExpr :: Can.Expr -> [Error] -> [Error]
262checkExpr (A.At region expression) errors =
267 Can.VarTopLevel _ _ ->
273 Can.VarForeign _ _ _ ->
276 Can.VarCtor _ _ _ _ _ ->
279 Can.VarDebug _ _ _ ->
282 Can.VarOperator _ _ _ _ ->
298 foldr checkExpr errors entries
301 checkExpr expr errors
303 Can.Binop _ _ _ _ left right ->
305 checkExpr right errors
307 Can.Lambda args body ->
308 foldr checkArg (checkExpr body errors) args
310 Can.Call func args ->
311 checkExpr func $ foldr checkExpr errors args
313 Can.If branches finally ->
314 foldr checkIfBranch (checkExpr finally errors) branches
317 checkDef def $ checkExpr body errors
319 Can.LetRec defs body ->
320 foldr checkDef (checkExpr body errors) defs
322 Can.LetDestruct pattern@(A.At reg _) expr body ->
323 checkPatterns reg BadDestruct [pattern] $
324 checkExpr expr $ checkExpr body errors
326 Can.Case expr branches ->
327 checkExpr expr $ checkCases region branches errors
332 Can.Access record _ ->
333 checkExpr record errors
335 Can.Update _ record fields ->
336 checkExpr record $ Map.foldr checkField errors fields
339 Map.foldr checkExpr errors fields
344 Can.Tuple a b maybeC ->
362checkField :: Can.FieldUpdate -> [Error] -> [Error]
363checkField (Can.FieldUpdate _ expr) errors =
364 checkExpr expr errors
371checkIfBranch :: (Can.Expr, Can.Expr) -> [Error] -> [Error]
372checkIfBranch (condition, branch) errs =
373 checkExpr condition $ checkExpr branch errs
377-- CHECK CASE EXPRESSION
380checkCases :: A.Region -> [Can.CaseBranch] -> [Error] -> [Error]
381checkCases region branches errors =
383 (patterns, newErrors) =
384 foldr checkCaseBranch ([], errors) branches
386 checkPatterns region BadCase patterns newErrors
389checkCaseBranch :: Can.CaseBranch -> ([Can.Pattern], [Error]) -> ([Can.Pattern], [Error])
390checkCaseBranch (Can.CaseBranch pattern expr) (patterns, errors) =
392 , checkExpr expr errors
400checkPatterns :: A.Region -> Context -> [Can.Pattern] -> [Error] -> [Error]
401checkPatterns region context patterns errors =
402 case toNonRedundantRows region patterns of
407 case isExhaustive matrix 1 of
412 Incomplete region context (map head badPatterns) : errors
416-- EXHAUSTIVE PATTERNS
421-- The initial rows "matrix" are all of length 1
422-- The initial count of items per row "n" is also 1
423-- The resulting rows are examples of missing patterns
425isExhaustive :: [[Pattern]] -> Int -> [[Pattern]]
426isExhaustive matrix n =
429 [replicate n Anything]
436 ctors = collectCtors matrix
437 numSeen = Map.size ctors
441 <$> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
444 let alts@(Can.Union _ altList numAlts _) = snd (Map.findMin ctors) in
445 if numSeen < numAlts then
447 <$> Maybe.mapMaybe (isMissing alts ctors) altList
448 <*> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
452 isAltExhaustive (Can.Ctor name _ arity _) =
453 recoverCtor alts name arity <$>
455 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
458 concatMap isAltExhaustive altList
461isMissing :: Can.Union -> Map.Map Name.Name a -> Can.Ctor -> Maybe Pattern
462isMissing union ctors (Can.Ctor name _ arity _) =
463 if Map.member name ctors then
466 Just (Ctor union name (replicate arity Anything))
469recoverCtor :: Can.Union -> Name.Name -> Int -> [Pattern] -> [Pattern]
470recoverCtor union name arity patterns =
473 splitAt arity patterns
475 Ctor union name args : rest
482-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
483toNonRedundantRows :: A.Region -> [Can.Pattern] -> Either Error [[Pattern]]
484toNonRedundantRows region patterns =
485 toSimplifiedUsefulRows region [] patterns
488-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
489toSimplifiedUsefulRows :: A.Region -> [[Pattern]] -> [Can.Pattern] -> Either Error [[Pattern]]
490toSimplifiedUsefulRows overallRegion checkedRows uncheckedPatterns =
491 case uncheckedPatterns of
495 pattern@(A.At region _) : rest ->
496 let nextRow = [simplify pattern] in
497 if isUseful checkedRows nextRow then
498 toSimplifiedUsefulRows overallRegion (nextRow : checkedRows) rest
500 Left (Redundant overallRegion region (length checkedRows + 1))
503-- Check if a new row "vector" is useful given previous rows "matrix"
504isUseful :: [[Pattern]] -> [Pattern] -> Bool
505isUseful matrix vector =
508 -- No rows are the same as the new vector! The vector is useful!
514 -- There is nothing left in the new vector, but we still have
515 -- rows that match the same things. This is not a useful vector!
518 firstPattern : patterns ->
521 -- keep checking rows that start with this Ctor or Anything
523 (Maybe.mapMaybe (specializeRowByCtor name (length args)) matrix)
527 -- check if all alts appear in matrix
528 case isComplete matrix of
530 -- This Anything is useful because some Ctors are missing.
531 -- But what if a previous row has an Anything?
532 -- If so, this one is not useful.
533 isUseful (Maybe.mapMaybe specializeRowByAnything matrix) patterns
536 -- All Ctors are covered, so this Anything is not needed for any
537 -- of those. But what if some of those Ctors have subpatterns
538 -- that make them less general? If so, this actually is useful!
540 isUsefulAlt (Can.Ctor name _ arity _) =
542 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
543 (replicate arity Anything ++ patterns)
548 -- keep checking rows that start with this Literal or Anything
550 (Maybe.mapMaybe (specializeRowByLiteral literal) matrix)
554-- INVARIANT: (length row == N) ==> (length result == arity + N - 1)
555specializeRowByCtor :: Name.Name -> Int -> [Pattern] -> Maybe [Pattern]
556specializeRowByCtor ctorName arity row =
558 Ctor _ name args : patterns ->
559 if name == ctorName then
560 Just (args ++ patterns)
564 Anything : patterns ->
565 Just (replicate arity Anything ++ patterns)
569 "Compiler bug! After type checking, constructors and literals\
570 \ should never align in pattern match exhaustiveness checks."
573 error "Compiler error! Empty matrices should not get specialized."
576-- INVARIANT: (length row == N) ==> (length result == N-1)
577specializeRowByLiteral :: Literal -> [Pattern] -> Maybe [Pattern]
578specializeRowByLiteral literal row =
580 Literal lit : patterns ->
581 if lit == literal then
586 Anything : patterns ->
591 "Compiler bug! After type checking, constructors and literals\
592 \ should never align in pattern match exhaustiveness checks."
595 error "Compiler error! Empty matrices should not get specialized."
598-- INVARIANT: (length row == N) ==> (length result == N-1)
599specializeRowByAnything :: [Pattern] -> Maybe [Pattern]
600specializeRowByAnything row =
608 Anything : patterns ->
616-- ALL CONSTRUCTORS ARE PRESENT?
624isComplete :: [[Pattern]] -> Complete
627 ctors = collectCtors matrix
628 numSeen = Map.size ctors
633 let (Can.Union _ alts numAlts _) = snd (Map.findMin ctors) in
634 if numSeen == numAlts then Yes alts else No
641collectCtors :: [[Pattern]] -> Map.Map Name.Name Can.Union
643 List.foldl' collectCtorsHelp Map.empty matrix
646collectCtorsHelp :: Map.Map Name.Name Can.Union -> [Pattern] -> Map.Map Name.Name Can.Union
647collectCtorsHelp ctors row =
649 Ctor union name _ : _ ->
650 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