1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Nitpick.PatternMatches
12{- The algorithm used here comes from "Warnings for Pattern Matching"
13by Luc Maranget. Check it out for more information!
15http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
19import qualified Data.List as List
20import qualified Data.Map as Map
21import qualified Data.Maybe as Maybe
22import qualified Data.Text as Text
24import qualified AST.Canonical as Can
25import qualified Elm.Name as N
26import qualified Reporting.Annotation as A
27import qualified Reporting.Region as R
37 | Ctor Can.CtorAlts N.Name [Pattern]
49-- CREATE SIMPLIFIED PATTERNS
52simplify :: Can.Pattern -> Pattern
53simplify (A.At _ pattern) =
65 Ctor unitAlts unitName []
67 Can.PTuple a b Nothing ->
68 Ctor pairAlts pairName [ simplify a, simplify b ]
70 Can.PTuple a b (Just c) ->
71 Ctor tripleAlts tripleName [ simplify a, simplify b, simplify c ]
73 Can.PCtor _ _ _ alts name args ->
75 map (\(Can.PatternCtorArg _ _ arg) -> simplify arg) args
78 foldr cons nil entries
83 Can.PAlias subPattern _ ->
96cons :: Can.Pattern -> Pattern -> Pattern
98 Ctor listAlts consName [ simplify hd, tl ]
104 Ctor listAlts nilName []
111{-# NOINLINE unitAlts #-}
112unitAlts :: Can.CtorAlts
114 Can.toAlts [ Can.CtorAlt unitName 0 ]
117{-# NOINLINE pairAlts #-}
118pairAlts :: Can.CtorAlts
120 Can.toAlts [ Can.CtorAlt pairName 2 ]
123{-# NOINLINE tripleAlts #-}
124tripleAlts :: Can.CtorAlts
126 Can.toAlts [ Can.CtorAlt tripleName 3 ]
129{-# NOINLINE listAlts #-}
130listAlts :: Can.CtorAlts
132 Can.toAlts [ Can.CtorAlt consName 2, Can.CtorAlt nilName 0 ]
135{-# NOINLINE unitName #-}
140{-# NOINLINE pairName #-}
145{-# NOINLINE tripleName #-}
150{-# NOINLINE consName #-}
155{-# NOINLINE nilName #-}
165 = Incomplete R.Region Context [Pattern]
166 | Redundant R.Region R.Region Int
179check :: Can.Module -> Either [Error] ()
180check (Can.Module _ _ _ decls _ _ _ _) =
181 case checkDecls decls [] of
193checkDecls :: Can.Decls -> [Error] -> [Error]
194checkDecls decls errors =
196 Can.Declare def subDecls ->
197 checkDef def $ checkDecls subDecls errors
199 Can.DeclareRec defs subDecls ->
200 foldr checkDef (checkDecls subDecls errors) defs
202 Can.SaveTheEnvironment ->
210checkDef :: Can.Def -> [Error] -> [Error]
213 Can.Def _ args _ body ->
214 foldr checkArg (checkExpr body errors) args
216 Can.TypedDef _ _ args _ body _ ->
217 foldr checkTypedArg (checkExpr body errors) args
220checkArg :: Can.Arg -> [Error] -> [Error]
221checkArg (Can.Arg _ pattern@(A.At region _)) errors =
222 checkPatterns region BadArg [pattern] errors
225checkTypedArg :: Can.TypedArg -> [Error] -> [Error]
226checkTypedArg (Can.TypedArg _ _ pattern@(A.At region _)) errors =
227 checkPatterns region BadArg [pattern] errors
234checkExpr :: Can.Expr -> [Error] -> [Error]
235checkExpr (A.At region expression) errors =
240 Can.VarTopLevel _ _ ->
246 Can.VarForeign _ _ _ ->
249 Can.VarOperator _ _ _ _ ->
265 foldr checkExpr errors entries
268 checkExpr expr errors
270 Can.Binop _ _ _ _ left right ->
272 checkExpr right errors
274 Can.Lambda args _ body ->
275 foldr checkArg (checkExpr body errors) args
277 Can.Call func args ->
278 checkExpr func $ foldr checkExpr errors args
280 Can.If branches finally ->
281 foldr checkIfBranch (checkExpr finally errors) branches
284 checkDef def $ checkExpr body errors
286 Can.LetRec defs body ->
287 foldr checkDef (checkExpr body errors) defs
289 Can.LetDestruct pattern@(A.At reg _) _ expr body ->
290 checkPatterns reg BadDestruct [pattern] $
291 checkExpr expr $ checkExpr body errors
293 Can.Case expr branches ->
294 checkExpr expr $ checkCases region branches errors
299 Can.Access record _ ->
300 checkExpr record errors
302 Can.Update record fields ->
303 checkExpr record $ Map.foldr checkExpr errors fields
306 Map.foldr checkExpr errors fields
311 Can.Tuple a b maybeC ->
329checkIfBranch :: (Can.Expr, Can.Expr) -> [Error] -> [Error]
330checkIfBranch (condition, branch) errs =
331 checkExpr condition $ checkExpr branch errs
335-- CHECK CASE EXPRESSION
338checkCases :: R.Region -> [Can.CaseBranch] -> [Error] -> [Error]
339checkCases region branches errors =
341 (patterns, newErrors) =
342 foldr checkCaseBranch ([], errors) branches
344 checkPatterns region BadCase patterns newErrors
347checkCaseBranch :: Can.CaseBranch -> ([Can.Pattern], [Error]) -> ([Can.Pattern], [Error])
348checkCaseBranch (Can.CaseBranch pattern _ expr) (patterns, errors) =
350 , checkExpr expr errors
358checkPatterns :: R.Region -> Context -> [Can.Pattern] -> [Error] -> [Error]
359checkPatterns region context patterns errors =
360 case toNonRedundantRows region patterns of
365 case isExhaustive matrix 1 of
370 Incomplete region context (map head badPatterns) : errors
374-- EXHAUSTIVE PATTERNS
379-- The initial rows "matrix" are all of length 1
380-- The initial count of items per row "n" is also 1
381-- The resulting rows are examples of missing patterns
383isExhaustive :: [[Pattern]] -> Int -> [[Pattern]]
384isExhaustive matrix n =
387 [replicate n Anything]
394 ctors = collectCtors matrix
395 numSeen = Map.size ctors
399 <$> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
402 let alts@(Can.CtorAlts numAlts altList) = snd (Map.findMin ctors) in
403 if numSeen < numAlts then
405 <$> Maybe.mapMaybe (isMissing alts ctors) altList
406 <*> isExhaustive (Maybe.mapMaybe specializeRowByAnything matrix) (n - 1)
410 isAltExhaustive (Can.CtorAlt name arity) =
411 recoverCtor alts name arity <$>
413 (Maybe.mapMaybe (specializeRowByCtor name arity) matrix)
416 concatMap isAltExhaustive altList
419isMissing :: Can.CtorAlts -> Map.Map N.Name a -> Can.CtorAlt -> Maybe Pattern
420isMissing alts ctors (Can.CtorAlt name arity) =
421 if Map.member name ctors then
424 Just (Ctor alts name (replicate arity Anything))
427recoverCtor :: Can.CtorAlts -> N.Name -> Int -> [Pattern] -> [Pattern]
428recoverCtor alts name arity patterns =
431 splitAt arity patterns
433 Ctor alts name args : rest
440-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
441toNonRedundantRows :: R.Region -> [Can.Pattern] -> Either Error [[Pattern]]
442toNonRedundantRows region patterns =
443 toSimplifiedUsefulRows region [] patterns
446-- INVARIANT: Produces a list of rows where (forall row. length row == 1)
447toSimplifiedUsefulRows :: R.Region -> [[Pattern]] -> [Can.Pattern] -> Either Error [[Pattern]]
448toSimplifiedUsefulRows overallRegion checkedRows uncheckedPatterns =
449 case uncheckedPatterns of
453 pattern@(A.At region _) : rest ->
454 let nextRow = [simplify pattern] in
455 if isUseful checkedRows nextRow then
456 toSimplifiedUsefulRows overallRegion (nextRow : checkedRows) rest
458 Left (Redundant overallRegion region (length checkedRows + 1))
461-- Check if a new row "vector" is useful given previous rows "matrix"
462isUseful :: [[Pattern]] -> [Pattern] -> Bool
463isUseful matrix vector =
466 -- No rows are the same as the new vector! The vector is useful!
472 -- There is nothing left in the new vector, but we still have
473 -- rows that match the same things. This is not a useful vector!
476 firstPattern : patterns ->
479 -- keep checking rows that start with this Ctor or Anything
481 (Maybe.mapMaybe (specializeRowByCtor name (length args)) matrix)
485 -- check if all alts appear in matrix
486 case isComplete matrix of
488 -- This Anything is useful because some Ctors are missing.
489 -- But what if a previous row has an Anything?
490 -- If so, this one is not useful.
491 isUseful (Maybe.mapMaybe specializeRowByAnything matrix) patterns
494 -- All Ctors are covered, so this Anything is not needed for any
495 -- of those. But what if some of those Ctors have subpatterns
496 -- that make them less general? If so, this actually is useful!
498 isUsefulAlt (Can.CtorAlt ctor arity) =
500 (Maybe.mapMaybe (specializeRowByCtor ctor arity) matrix)
501 (replicate arity Anything ++ patterns)
506 -- keep checking rows that start with this Literal or Anything
508 (Maybe.mapMaybe (specializeRowByLiteral literal) matrix)
512-- INVARIANT: (length row == N) ==> (length result == arity + N - 1)
513specializeRowByCtor :: N.Name -> Int -> [Pattern] -> Maybe [Pattern]
514specializeRowByCtor ctorName arity row =
516 Ctor _ name args : patterns ->
517 if name == ctorName then
518 Just (args ++ patterns)
522 Anything : patterns ->
523 Just (replicate arity Anything ++ patterns)
527 "Compiler bug! After type checking, constructors and literals\
528 \ should never align in pattern match exhaustiveness checks."
531 error "Compiler error! Empty matrices should not get specialized."
534-- INVARIANT: (length row == N) ==> (length result == N-1)
535specializeRowByLiteral :: Literal -> [Pattern] -> Maybe [Pattern]
536specializeRowByLiteral literal row =
538 Literal lit : patterns ->
539 if lit == literal then
544 Anything : patterns ->
549 "Compiler bug! After type checking, constructors and literals\
550 \ should never align in pattern match exhaustiveness checks."
553 error "Compiler error! Empty matrices should not get specialized."
556-- INVARIANT: (length row == N) ==> (length result == N-1)
557specializeRowByAnything :: [Pattern] -> Maybe [Pattern]
558specializeRowByAnything row =
566 Anything : patterns ->
574-- ALL CONSTRUCTORS ARE PRESENT?
582isComplete :: [[Pattern]] -> Complete
585 ctors = collectCtors matrix
586 numSeen = Map.size ctors
591 let (Can.CtorAlts numAlts alts) = snd (Map.findMin ctors) in
592 if numSeen == numAlts then Yes alts else No
599collectCtors :: [[Pattern]] -> Map.Map N.Name Can.CtorAlts
601 List.foldl' collectCtorsHelp Map.empty matrix
604collectCtorsHelp :: Map.Map N.Name Can.CtorAlts -> [Pattern] -> Map.Map N.Name Can.CtorAlts
605collectCtorsHelp ctors row =
607 Ctor info name _ : _ ->
608 Map.insert name info 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