@@ -0,0 +1,270 @@
1+{-# LANGUAGE NamedFieldPuns, TupleSections #-}
2+module Nitpick.PatternMatches
8+import Control.Applicative ((<$>), (<*>))
9+import Control.Monad.Except (forM, liftM, throwError)
10+import Data.Maybe (mapMaybe, catMaybes)
11+import qualified Data.Set as Set
12+import qualified Data.Map as Map
14+import qualified AST.Expression.General as General
15+import qualified AST.Expression.Canonical as Canonical
16+import qualified AST.Module as Module
17+import qualified AST.Pattern as Pattern
18+import qualified AST.Variable as Var
19+import Elm.Utils ((|>))
20+import qualified Reporting.Annotation as Annotation
21+import qualified Reporting.Error.Nitpick as Error
22+import qualified Reporting.Region as Region
23+import qualified Reporting.Warning.Nitpick as Warning
27+ Either (Annotation.Located Error.Error)
30+fromPattern :: CanonicalPattern -> CanonicalPat
31+fromPattern (Annotation.A _region p) =
33+ Pattern.Data ctor ps ->
34+ Data ctor (map fromPattern ps)
40+ Alias s (fromPattern p)
54+ -> Module.CanonicalBody
55+ -> M [Annotation.Located Warning]
56+checkBody interfaces =
57+ checkExpr . Module.program
59+ checkExpr :: Canonical.Expr -> M [Annotation.Located Warning]
60+ checkExpr (Annotation.A r e) =
62+ General.ExplicitList es ->
63+ concatMapM checkExpr es
65+ General.Binop _ e e' ->
66+ (++) <$> checkExpr e <*> checkExpr e'
68+ General.Lambda p e ->
69+ maybeCons <$> checkMatch r [p] <*> checkExpr e
72+ (++) <$> checkExpr e <*> checkExpr e'
74+ General.MultiIf branches ->
75+ concatMapM (\(b,e) -> (++) <$> checkExpr b <*> checkExpr e) branches
77+ General.Let defs e ->
78+ concat <$> sequence [ps, rhssResults, checkExpr e]
80+ ps = filterMapM (\(Canonical.Definition p _ _) -> checkMatch r [p]) defs
82+ rhssResults = concatMapM (\(Canonical.Definition _ e _) -> checkExpr e) defs
84+ General.Case e branches ->
85+ do eResults <- checkExpr e
86+ result <- checkMatch r ps
87+ branchResults <- concatMapM (checkExpr . snd) branches
88+ return (eResults ++ maybeCons result branchResults)
89+ where ps = map fst branches
91+ General.Data _ctor es ->
92+ concatMapM checkExpr es
94+ General.Access e _f ->
97+ General.Remove e _f ->
100+ General.Insert e _f e' ->
101+ (++) <$> checkExpr e <*> checkExpr e'
103+ General.Modify e _ ->
106+ General.Record fields ->
107+ concatMapM (checkExpr . snd) fields
113+ ctorToAdt :: Var.Canonical -> Maybe (Module.AdtInfo String)
115+ \(Var.Canonical {Var.home,Var.name}) -> Map.lookup name =<< Map.lookup home m
117+ -- Maps home to ctor name to the adt to which that ctor belongs
118+ m :: Map.Map Var.Home (Map.Map String (Module.AdtInfo String))
120+ builtins `Map.union` fromModules
123+ builtins = Map.empty
125+ fromModules = Map.mapKeysMonotonic Var.Module (Map.map Module.iAdts interfaces)
128+ ctorToAdtErr :: Region.Region -> Var.Canonical -> M (Module.AdtInfo String)
129+ ctorToAdtErr region c =
132+ throwError (Annotation.A region (Error.ConstructorTypeNotFound c))
138+ complement :: Region.Region -> CanonicalPat -> M [CanonicalPat]
139+ complement region p =
142+ do let ctorName = Var.name ctor
143+ ctorHome = Var.home ctor
144+ (_tyVars, ctors) <- ctorToAdtErr region ctor
145+ -- A pattern is in the complement of this one if it uses one of the
146+ -- other constructors of this type...
147+ let otherCtorPat (ctor', args) =
148+ Data (Var.Canonical {Var.name = ctor', Var.home = ctorHome})
149+ (replicate (length args) Anything)
152+ filter (\(ctor', _) -> ctor' /= ctorName) ctors
155+ -- or if it uses this constructor and one of its argument patterns is
156+ -- in the complement of an argument pattern here.
158+ sandwich pat i = replicate i Anything ++ pat : replicate (numArgs - i - 1) Anything
160+ argComplement <- fmap concat . forM (zip [0..] ps) $ \(i, argP) ->
161+ map (\argP' -> Data ctor (sandwich argP' i)) <$> (complement region argP)
163+ return (otherCtorPatterns ++ argComplement)
178+ return [AnythingBut (Set.singleton l)]
181+ return (map Literal (Set.toList ls))
183+ intersection :: CanonicalPat -> CanonicalPat -> Maybe CanonicalPat
184+ intersection p1 p2 =
204+ (Data ctor1 args1, Data ctor2 args2) ->
207+ else fmap (Data ctor1) (sequence (zipWith intersection args1 args2))
209+ -- TODO Assuming the fields are identical.
210+ (Record _fs, Record _) ->
213+ (Literal l1, Literal l2) ->
214+ if l1 == l2 then Just p1 else Nothing
216+ (AnythingBut ls, Literal l) ->
217+ if l `Set.member` ls then Nothing else Just p2
219+ (Literal l, AnythingBut ls) ->
220+ if l `Set.member` ls then Nothing else Just p1
222+ (AnythingBut ls1, AnythingBut ls2) ->
223+ Just (AnythingBut (Set.union ls1 ls2))
227+ checkMatch :: Region.Region -> [CanonicalPattern] -> M (Maybe (Annotation.Located Warning))
231+ go :: [CanonicalPat] -> [CanonicalPattern] -> M (Maybe (Annotation.Located Warning))
233+ case (unhandled, ps) of
238+ return (Just (Annotation.A region (Inexhaustive unhandled)))
241+ let pp = fromPattern p
242+ Annotation.A region _ = p
244+ if all (\q -> intersection q pp == Nothing) unhandled
245+ then return (Just (Annotation.A region (Redundant p)))
247+ do pComp <- complement region pp
248+ go (concatMap (\q -> mapMaybe (intersection q) pComp) unhandled) ps'
253+concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
255+ liftM concat (mapM f list)
258+filterMapM :: Monad m => (a -> m (Maybe b)) -> [a] -> m [b]
260+ liftM catMaybes (mapM f list)
263+maybeCons :: Maybe a -> [a] -> [a]
264+maybeCons maybe list =