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.Text as Text
25import qualified AST.Canonical as Can
26import qualified AST.Module.Name as ModuleName
27import qualified Data.Index as Index
28import qualified Elm.Name as N
29import qualified Reporting.Annotation as A
30import qualified Reporting.Region as R
40 | Ctor Can.Union N.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 _ ->
98cons :: Can.Pattern -> Pattern -> Pattern
100 Ctor list consName [ simplify hd, tl ]
118 Can.Ctor unitName Index.first 0 []
120 Can.Union [] [ ctor ] 1 Can.Normal
128 Can.Ctor pairName Index.first 2 [Can.TVar "a", Can.TVar "b"]
130 Can.Union ["a","b"] [ ctor ] 1 Can.Normal
133{-# NOINLINE triple #-}
138 Can.Ctor tripleName Index.first 3 [Can.TVar "a", Can.TVar "b", Can.TVar "c"]
140 Can.Union ["a","b","c"] [ ctor ] 1 Can.Normal
148 Can.Ctor nilName Index.first 0 []
151 Can.Ctor consName Index.second 2
153 , Can.TType ModuleName.list N.list [Can.TVar "a"]
156 Can.Union ["a"] [ nilCtor, consCtor ] 2 Can.Normal
159{-# NOINLINE unitName #-}
164{-# NOINLINE pairName #-}
169{-# NOINLINE tripleName #-}
174{-# NOINLINE consName #-}
179{-# NOINLINE nilName #-}
189 = Incomplete R.Region Context [Pattern]
190 | Redundant R.Region R.Region Int
203check :: Can.Module -> Either [Error] ()
204check (Can.Module _ _ _ decls _ _ _ _) =
205 case checkDecls decls [] of
217checkDecls :: Can.Decls -> [Error] -> [Error]
218checkDecls decls errors =
220 Can.Declare def subDecls ->
221 checkDef def $ checkDecls subDecls errors
223 Can.DeclareRec defs subDecls ->
224 foldr checkDef (checkDecls subDecls errors) defs
226 Can.SaveTheEnvironment ->
234checkDef :: Can.Def -> [Error] -> [Error]
237 Can.Def _ args body ->
238 foldr checkArg (checkExpr body errors) args
240 Can.TypedDef _ _ args body _ ->
241 foldr checkTypedArg (checkExpr body errors) args
244checkArg :: Can.Pattern -> [Error] -> [Error]
245checkArg pattern@(A.At region _) errors =
246 checkPatterns region BadArg [pattern] errors
249checkTypedArg :: (Can.Pattern, tipe) -> [Error] -> [Error]
250checkTypedArg (pattern@(A.At region _), _) errors =
251 checkPatterns region BadArg [pattern] errors
258checkExpr :: Can.Expr -> [Error] -> [Error]
259checkExpr (A.At region expression) errors =
264 Can.VarTopLevel _ _ ->
270 Can.VarForeign _ _ _ ->
273 Can.VarCtor _ _ _ _ _ ->
276 Can.VarDebug _ _ _ ->
279 Can.VarOperator _ _ _ _ ->
295 foldr checkExpr errors entries
298 checkExpr expr errors
300 Can.Binop _ _ _ _ left right ->
302 checkExpr right errors
304 Can.Lambda args body ->
305 foldr checkArg (checkExpr body errors) args
307 Can.Call func args ->
308 checkExpr func $ foldr checkExpr errors args
310 Can.If branches finally ->
311 foldr checkIfBranch (checkExpr finally errors) branches
314 checkDef def $ checkExpr body errors
316 Can.LetRec defs body ->
317 foldr checkDef (checkExpr body errors) defs
319 Can.LetDestruct pattern@(A.At reg _) expr body ->
320 checkPatterns reg BadDestruct [pattern] $
321 checkExpr expr $ checkExpr body errors
323 Can.Case expr branches ->
324 checkExpr expr $ checkCases region branches errors
329 Can.Access record _ ->
330 checkExpr record errors
332 Can.Update record fields ->
333 checkExpr record $ Map.foldr checkExpr errors fields
336 Map.foldr checkExpr errors fields
341 Can.Tuple a b maybeC ->
359checkIfBranch :: (Can.Expr, Can.Expr) -> [Error] -> [Error]
360checkIfBranch (condition, branch) errs =
361 checkExpr condition $ checkExpr branch errs
365-- CHECK CASE EXPRESSION
368checkCases :: R.Region -> [Can.CaseBranch] -> [Error] -> [Error]
369checkCases region branches errors =
371 (patterns, newErrors) =
372 foldr checkCaseBranch ([], errors) branches
374 checkPatterns region BadCase patterns newErrors
377checkCaseBranch :: Can.CaseBranch -> ([Can.Pattern], [Error]) -> ([Can.Pattern], [Error])
378checkCaseBranch (Can.CaseBranch pattern expr) (patterns, errors) =
380 , checkExpr expr errors
388checkPatterns :: R.Region -> Context -> [Can.Pattern] -> [Error] -> [Error]
389checkPatterns region context patterns errors =
390 case toNonRedundantRows region patterns of
395 case isExhaustive matrix 1 of
400 Incomplete region context (map head badPatterns) : errors
404-- EXHAUSTIVE PATTERNS
409-- The initial rows "matrix" are all of length 1
410-- The initial count of items per row "n" is also 1
411-- The resulting rows are examples of missing patterns
413isExhaustive :: [[Pattern]] -> Int -> [[Pattern]]
414isExhaustive matrix n =
417 [replicate n Anything]
424 ctors = collectCtors matrix
425 numSeen = Map.size ctors
429 <$> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
432 let alts@(Can.Union _ altList numAlts _) = snd (Map.findMin ctors) in
433 if numSeen < numAlts then
435 <$> Maybe.mapMaybe (isMissing alts ctors) altList
436 <*> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
440 isAltExhaustive (Can.Ctor name _ arity _) =
441 recoverCtor alts name arity <$>
443 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
446 concatMap isAltExhaustive altList
449isMissing :: Can.Union -> Map.Map N.Name a -> Can.Ctor -> Maybe Pattern
450isMissing union ctors (Can.Ctor name _ arity _) =
451 if Map.member name ctors then
454 Just (Ctor union name (replicate arity Anything))
457recoverCtor :: Can.Union -> N.Name -> Int -> [Pattern] -> [Pattern]
458recoverCtor union name arity patterns =
461 splitAt arity patterns
463 Ctor union name args : rest
470-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
471toNonRedundantRows :: R.Region -> [Can.Pattern] -> Either Error [[Pattern]]
472toNonRedundantRows region patterns =
473 toSimplifiedUsefulRows region [] patterns
476-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
477toSimplifiedUsefulRows :: R.Region -> [[Pattern]] -> [Can.Pattern] -> Either Error [[Pattern]]
478toSimplifiedUsefulRows overallRegion checkedRows uncheckedPatterns =
479 case uncheckedPatterns of
483 pattern@(A.At region _) : rest ->
484 let nextRow = [simplify pattern] in
485 if isUseful checkedRows nextRow then
486 toSimplifiedUsefulRows overallRegion (nextRow : checkedRows) rest
488 Left (Redundant overallRegion region (length checkedRows + 1))
491-- Check if a new row "vector" is useful given previous rows "matrix"
492isUseful :: [[Pattern]] -> [Pattern] -> Bool
493isUseful matrix vector =
496 -- No rows are the same as the new vector! The vector is useful!
502 -- There is nothing left in the new vector, but we still have
503 -- rows that match the same things. This is not a useful vector!
506 firstPattern : patterns ->
509 -- keep checking rows that start with this Ctor or Anything
511 (Maybe.mapMaybe (specializeRowByCtor name (length args)) matrix)
515 -- check if all alts appear in matrix
516 case isComplete matrix of
518 -- This Anything is useful because some Ctors are missing.
519 -- But what if a previous row has an Anything?
520 -- If so, this one is not useful.
521 isUseful (Maybe.mapMaybe specializeRowByAnything matrix) patterns
524 -- All Ctors are covered, so this Anything is not needed for any
525 -- of those. But what if some of those Ctors have subpatterns
526 -- that make them less general? If so, this actually is useful!
528 isUsefulAlt (Can.Ctor name _ arity _) =
530 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
531 (replicate arity Anything ++ patterns)
536 -- keep checking rows that start with this Literal or Anything
538 (Maybe.mapMaybe (specializeRowByLiteral literal) matrix)
542-- INVARIANT: (length row == N) ==> (length result == arity + N - 1)
543specializeRowByCtor :: N.Name -> Int -> [Pattern] -> Maybe [Pattern]
544specializeRowByCtor ctorName arity row =
546 Ctor _ name args : patterns ->
547 if name == ctorName then
548 Just (args ++ patterns)
552 Anything : patterns ->
553 Just (replicate arity Anything ++ patterns)
557 "Compiler bug! After type checking, constructors and literals\
558 \ should never align in pattern match exhaustiveness checks."
561 error "Compiler error! Empty matrices should not get specialized."
564-- INVARIANT: (length row == N) ==> (length result == N-1)
565specializeRowByLiteral :: Literal -> [Pattern] -> Maybe [Pattern]
566specializeRowByLiteral literal row =
568 Literal lit : patterns ->
569 if lit == literal then
574 Anything : patterns ->
579 "Compiler bug! After type checking, constructors and literals\
580 \ should never align in pattern match exhaustiveness checks."
583 error "Compiler error! Empty matrices should not get specialized."
586-- INVARIANT: (length row == N) ==> (length result == N-1)
587specializeRowByAnything :: [Pattern] -> Maybe [Pattern]
588specializeRowByAnything row =
596 Anything : patterns ->
604-- ALL CONSTRUCTORS ARE PRESENT?
612isComplete :: [[Pattern]] -> Complete
615 ctors = collectCtors matrix
616 numSeen = Map.size ctors
621 let (Can.Union _ alts numAlts _) = snd (Map.findMin ctors) in
622 if numSeen == numAlts then Yes alts else No
629collectCtors :: [[Pattern]] -> Map.Map N.Name Can.Union
631 List.foldl' collectCtorsHelp Map.empty matrix
634collectCtorsHelp :: Map.Map N.Name Can.Union -> [Pattern] -> Map.Map N.Name Can.Union
635collectCtorsHelp ctors row =
637 Ctor union name _ : _ ->
638 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