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 checkField errors fields
336 Map.foldr checkExpr errors fields
341 Can.Tuple a b maybeC ->
359checkField :: Can.FieldUpdate -> [Error] -> [Error]
360checkField (Can.FieldUpdate _ expr) errors =
361 checkExpr expr errors
368checkIfBranch :: (Can.Expr, Can.Expr) -> [Error] -> [Error]
369checkIfBranch (condition, branch) errs =
370 checkExpr condition $ checkExpr branch errs
374-- CHECK CASE EXPRESSION
377checkCases :: R.Region -> [Can.CaseBranch] -> [Error] -> [Error]
378checkCases region branches errors =
380 (patterns, newErrors) =
381 foldr checkCaseBranch ([], errors) branches
383 checkPatterns region BadCase patterns newErrors
386checkCaseBranch :: Can.CaseBranch -> ([Can.Pattern], [Error]) -> ([Can.Pattern], [Error])
387checkCaseBranch (Can.CaseBranch pattern expr) (patterns, errors) =
389 , checkExpr expr errors
397checkPatterns :: R.Region -> Context -> [Can.Pattern] -> [Error] -> [Error]
398checkPatterns region context patterns errors =
399 case toNonRedundantRows region patterns of
404 case isExhaustive matrix 1 of
409 Incomplete region context (map head badPatterns) : errors
413-- EXHAUSTIVE PATTERNS
418-- The initial rows "matrix" are all of length 1
419-- The initial count of items per row "n" is also 1
420-- The resulting rows are examples of missing patterns
422isExhaustive :: [[Pattern]] -> Int -> [[Pattern]]
423isExhaustive matrix n =
426 [replicate n Anything]
433 ctors = collectCtors matrix
434 numSeen = Map.size ctors
438 <$> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
441 let alts@(Can.Union _ altList numAlts _) = snd (Map.findMin ctors) in
442 if numSeen < numAlts then
444 <$> Maybe.mapMaybe (isMissing alts ctors) altList
445 <*> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
449 isAltExhaustive (Can.Ctor name _ arity _) =
450 recoverCtor alts name arity <$>
452 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
455 concatMap isAltExhaustive altList
458isMissing :: Can.Union -> Map.Map N.Name a -> Can.Ctor -> Maybe Pattern
459isMissing union ctors (Can.Ctor name _ arity _) =
460 if Map.member name ctors then
463 Just (Ctor union name (replicate arity Anything))
466recoverCtor :: Can.Union -> N.Name -> Int -> [Pattern] -> [Pattern]
467recoverCtor union name arity patterns =
470 splitAt arity patterns
472 Ctor union name args : rest
479-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
480toNonRedundantRows :: R.Region -> [Can.Pattern] -> Either Error [[Pattern]]
481toNonRedundantRows region patterns =
482 toSimplifiedUsefulRows region [] patterns
485-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
486toSimplifiedUsefulRows :: R.Region -> [[Pattern]] -> [Can.Pattern] -> Either Error [[Pattern]]
487toSimplifiedUsefulRows overallRegion checkedRows uncheckedPatterns =
488 case uncheckedPatterns of
492 pattern@(A.At region _) : rest ->
493 let nextRow = [simplify pattern] in
494 if isUseful checkedRows nextRow then
495 toSimplifiedUsefulRows overallRegion (nextRow : checkedRows) rest
497 Left (Redundant overallRegion region (length checkedRows + 1))
500-- Check if a new row "vector" is useful given previous rows "matrix"
501isUseful :: [[Pattern]] -> [Pattern] -> Bool
502isUseful matrix vector =
505 -- No rows are the same as the new vector! The vector is useful!
511 -- There is nothing left in the new vector, but we still have
512 -- rows that match the same things. This is not a useful vector!
515 firstPattern : patterns ->
518 -- keep checking rows that start with this Ctor or Anything
520 (Maybe.mapMaybe (specializeRowByCtor name (length args)) matrix)
524 -- check if all alts appear in matrix
525 case isComplete matrix of
527 -- This Anything is useful because some Ctors are missing.
528 -- But what if a previous row has an Anything?
529 -- If so, this one is not useful.
530 isUseful (Maybe.mapMaybe specializeRowByAnything matrix) patterns
533 -- All Ctors are covered, so this Anything is not needed for any
534 -- of those. But what if some of those Ctors have subpatterns
535 -- that make them less general? If so, this actually is useful!
537 isUsefulAlt (Can.Ctor name _ arity _) =
539 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
540 (replicate arity Anything ++ patterns)
545 -- keep checking rows that start with this Literal or Anything
547 (Maybe.mapMaybe (specializeRowByLiteral literal) matrix)
551-- INVARIANT: (length row == N) ==> (length result == arity + N - 1)
552specializeRowByCtor :: N.Name -> Int -> [Pattern] -> Maybe [Pattern]
553specializeRowByCtor ctorName arity row =
555 Ctor _ name args : patterns ->
556 if name == ctorName then
557 Just (args ++ patterns)
561 Anything : patterns ->
562 Just (replicate arity Anything ++ patterns)
566 "Compiler bug! After type checking, constructors and literals\
567 \ should never align in pattern match exhaustiveness checks."
570 error "Compiler error! Empty matrices should not get specialized."
573-- INVARIANT: (length row == N) ==> (length result == N-1)
574specializeRowByLiteral :: Literal -> [Pattern] -> Maybe [Pattern]
575specializeRowByLiteral literal row =
577 Literal lit : patterns ->
578 if lit == literal then
583 Anything : patterns ->
588 "Compiler bug! After type checking, constructors and literals\
589 \ should never align in pattern match exhaustiveness checks."
592 error "Compiler error! Empty matrices should not get specialized."
595-- INVARIANT: (length row == N) ==> (length result == N-1)
596specializeRowByAnything :: [Pattern] -> Maybe [Pattern]
597specializeRowByAnything row =
605 Anything : patterns ->
613-- ALL CONSTRUCTORS ARE PRESENT?
621isComplete :: [[Pattern]] -> Complete
624 ctors = collectCtors matrix
625 numSeen = Map.size ctors
630 let (Can.Union _ alts numAlts _) = snd (Map.findMin ctors) in
631 if numSeen == numAlts then Yes alts else No
638collectCtors :: [[Pattern]] -> Map.Map N.Name Can.Union
640 List.foldl' collectCtorsHelp Map.empty matrix
643collectCtorsHelp :: Map.Map N.Name Can.Union -> [Pattern] -> Map.Map N.Name Can.Union
644collectCtorsHelp ctors row =
646 Ctor union name _ : _ ->
647 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