@@ -1,270 +1,305 @@
1−{-# LANGUAGE NamedFieldPuns, TupleSections #-}
2−module Nitpick.PatternMatches
1+module Nitpick.PatternMatches (patternMatches) where
8−import Control.Applicative ((<$>), (<*>))
3+import Control.Applicative ((<$>), (<*))
9−import Control.Monad.Except (forM, liftM, throwError)
4+import Control.Monad.Except (forM, liftM)
10−import Data.Maybe (mapMaybe, catMaybes)
11−import qualified Data.Set as Set
5+import qualified Data.Foldable as F
12import qualified Data.Map as Map
6import qualified Data.Map as Map
7+import qualified Data.Maybe as Maybe
8+import qualified Data.Set as Set
14−import qualified AST.Expression.General as General
10+import qualified AST.Expression.General as E
15import qualified AST.Expression.Canonical as Canonical
11import qualified AST.Expression.Canonical as Canonical
16import qualified AST.Module as Module
12import qualified AST.Module as Module
17import qualified AST.Pattern as Pattern
13import qualified AST.Pattern as Pattern
18import qualified AST.Variable as Var
14import qualified AST.Variable as Var
19import Elm.Utils ((|>))
15import Elm.Utils ((|>))
16+import qualified Nitpick.Pattern as P
20−import qualified Reporting.Annotation as Annotation
17+import qualified Reporting.Annotation as A
21import qualified Reporting.Error.Nitpick as Error
18import qualified Reporting.Error.Nitpick as Error
22import qualified Reporting.Region as Region
19import qualified Reporting.Region as Region
20+import qualified Reporting.Result as Result
23import qualified Reporting.Warning.Nitpick as Warning
21import qualified Reporting.Warning.Nitpick as Warning
26−type M =
24+patternMatches
27− Either (Annotation.Located Error.Error)
25+ :: Module.Interfaces
28−
26+ -> Module.CanonicalBody
29−
27+ -> Result.Result Warning.Warning e ()
30−fromPattern :: CanonicalPattern -> CanonicalPat
28+patternMatches interfaces body =
31−fromPattern (Annotation.A _region p) =
29+ checkExpression (toAdtDict interfaces) (Module.program body)
33− Pattern.Data ctor ps ->
34− Data ctor (map fromPattern ps)
39− Pattern.Alias s p ->
32+-- ADT DICT
40− Alias s (fromPattern p)
42− Pattern.Var s ->
34+type AdtDict =
43− Var s
35+ Map.Map Var.Home (Map.Map String (Module.AdtInfo String))
48− Pattern.Literal l ->
38+toAdtDict :: Module.Interfaces -> AdtDict
49− Literal l
39+toAdtDict =
47+ |> Map.map Module.iAdts
48+ |> Map.mapKeysMonotonic Var.Module
50+ Map.union builtinDict interfaceDict
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' ->
53+-- TODO this is wrong
66− (++) <$> checkExpr e <*> checkExpr e'
54+lookupOtherCtors
57+ -> [(Var.Canonical, [Type.Canonical])]
58+lookupOtherCtors (Var.Canonical home name) adtDict =
59+ case Map.lookup name =<< Map.lookup home adtDict of
68− General.Lambda p e ->
63+ Just (_tvars, ctors) ->
69− maybeCons <$> checkMatch r [p] <*> checkExpr e
64+ ctors
72− (++) <$> checkExpr e <*> checkExpr e'
74− General.MultiIf branches ->
67+-- CHECK EXPRESSIONS
75− concatMapM (\(b,e) -> (++) <$> checkExpr b <*> checkExpr e) branches
77− General.Let defs e ->
69+checkExpression
78− concat <$> sequence [ps, rhssResults, checkExpr e]
70+ :: AdtDict
79− where
71+ -> Canonical.Expr
80− ps = filterMapM (\(Canonical.Definition p _ _) -> checkMatch r [p]) defs
72+ -> Result.Result Warning.Warning e ()
73+checkExpression adtDict (A.A region expression) =
76+ checkExpression adtDict
82− rhssResults = concatMapM (\(Canonical.Definition _ e _) -> checkExpr e) defs
78+ go2 a b =
84− General.Case e branches ->
81+ goPattern =
85− do eResults <- checkExpr e
82+ checkPatterns adtDict region
86− result <- checkMatch r ps
83+ in
87− branchResults <- concatMapM (checkExpr . snd) branches
84+ case expression of
88− return (eResults ++ maybeCons result branchResults)
85+ E.ExplicitList listExprs ->
89− where ps = map fst branches
86+ F.traverse_ go listExprs
91− General.Data _ctor es ->
88+ E.Binop _ leftExpr rightExpr ->
92− concatMapM checkExpr es
89+ go2 leftExpr rightExpr
94− General.Access e _f ->
91+ E.Lambda pattern body ->
95− checkExpr e
92+ goPattern [pattern]
97− General.Remove e _f ->
95+ E.App func arg ->
98− checkExpr e
96+ go2 func arg
100− General.Insert e _f e' ->
98+ E.MultiIf branches ->
101− (++) <$> checkExpr e <*> checkExpr e'
99+ F.traverse_ (uncurry go2) branches
103− General.Modify e _ ->
101+ E.Let defs body ->
104− checkExpr e
102+ go body
105−
103+ <* F.traverse_ goDef defs
106− General.Record fields ->
104+ where
107− concatMapM (checkExpr . snd) fields
105+ goDef (Canonical.Definition pattern expr _) =
109+ E.Case expr branches ->
111+ <* goPattern (map fst branches)
112+ <* F.traverse_ (go . snd) branches
114+ E.Data _ctor exprs ->
115+ F.traverse_ go exprs
117+ E.Access record _field ->
109− _ ->
120+ E.Remove record _field ->
110− return []
121+ go record
123+ E.Insert record _field value ->
126+ E.Modify record fields ->
128+ <* F.traverse_ (go . snd) fields
131+ F.traverse_ (go . snd) fields
139+ -> [Pattern.CanonicalPattern]
140+ -> Result.Result Warning.Warning e ()
141+checkPatterns adtDict region patterns =
142+ checkPatternsHelp [P.Anything] patterns
113− ctorToAdt :: Var.Canonical -> Maybe (Module.AdtInfo String)
145+checkPatternsHelp
114− ctorToAdt =
146+ :: AdtDict
115− \(Var.Canonical {Var.home,Var.name}) -> Map.lookup name =<< Map.lookup home m
147+ -> Region.Region
116− where
148+ -> [P.Pattern]
117− -- Maps home to ctor name to the adt to which that ctor belongs
149+ -> [Pattern.CanonicalPattern]
118− m :: Map.Map Var.Home (Map.Map String (Module.AdtInfo String))
150+ -> Result.Result Warning.Warning e ()
119− m =
151+checkPatternsHelp adtDict region unhandled patterns =
120− builtins `Map.union` fromModules
152+ case (unhandled, patterns) of
121− where
153+ ([], []) ->
122− -- TODO
154+ return ()
123− builtins = Map.empty
125− fromModules = Map.mapKeysMonotonic Var.Module (Map.map Module.iAdts interfaces)
156+ (_:_, []) ->
157+ Result.warn region (Inexhaustive unhandled)
159+ (_, A.A region pattern : remainingPatterns) ->
160+ do newUnhandled <- filterPatterns adtDict region pattern unhandled
161+ checkPatternsHelp adtDict newUnhandled remainingPatterns
128− ctorToAdtErr :: Region.Region -> Var.Canonical -> M (Module.AdtInfo String)
129− ctorToAdtErr region c =
132− throwError (Annotation.A region (Error.ConstructorTypeNotFound c))
134− Just adt ->
164+filterPatterns
135− return adt
165+ :: AdtDict
169+ -> Result.Result Warning.Warning e [P.Pattern]
170+filterPatterns adtDict region pattern unhandled =
173+ P.fromPattern pattern
175+ noIntersection pat =
176+ intersection pat nitPattern == Nothing
178+ if all noIntersection unhandled
180+ do Result.warn region Redundant
183+ do let complementPatterns = complement adtDict nitPattern
185+ concatMap (\p -> Maybe.mapMaybe (intersection p) complementPatterns) unhandled
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)
151− otherCtorPatterns =
188+-- PATTERN INTERSECTION
152− filter (\(ctor', _) -> ctor' /= ctorName) ctors
155− -- or if it uses this constructor and one of its argument patterns is
190+intersection :: P.Pattern -> P.Pattern -> Maybe P.Pattern
156− -- in the complement of an argument pattern here.
191+intersection pattern1 pattern2 =
157− numArgs = length ps
192+ case (pattern1, pattern2) of
158− sandwich pat i = replicate i Anything ++ pat : replicate (numArgs - i - 1) Anything
193+ (P.Alias _ pattern1', _) ->
194+ intersection pattern1' pattern2
160− argComplement <- fmap concat . forM (zip [0..] ps) $ \(i, argP) ->
196+ (_, P.Alias _ pattern2') ->
161− map (\argP' -> Data ctor (sandwich argP' i)) <$> (complement region argP)
197+ intersection pattern1 pattern2'
163− return (otherCtorPatterns ++ argComplement)
199+ (P.Anything, _) ->
165− Record _fs ->
202+ (P.Var _, _) ->
166− return []
203+ Just pattern2
168− Alias _s p ->
205+ (_, P.Anything) ->
169− complement region p
206+ Just pattern1
171− Var _s ->
208+ (_, P.Var _) ->
172− return []
209+ Just pattern1
174− Anything ->
211+ (P.Data ctor1 args1, P.Data ctor2 args2) ->
175− return []
212+ if ctor1 /= ctor2
214+ else Data ctor1 <$> sequence (zipWith intersection args1 args2)
177− Literal l ->
216+ -- TODO Assuming the fields are identical.
178− return [AnythingBut (Set.singleton l)]
217+ (P.Record _fields, P.Record _) ->
180− AnythingBut ls ->
220+ (P.Literal lit1, P.Literal lit2) ->
181− return (map Literal (Set.toList ls))
221+ if lit1 == lit2
183− intersection :: CanonicalPat -> CanonicalPat -> Maybe CanonicalPat
225+ (P.AnythingBut literals, P.Literal lit) ->
184− intersection p1 p2 =
226+ if Set.member lit literals
185− case (p1, p2) of
227+ then Nothing
186− (Alias _ p1', _) ->
228+ else Just pattern2
189− (_, Alias _ p2') ->
230+ (P.Literal lit, P.AnythingBut literals) ->
190− intersection p1 p2'
231+ if Set.membur lit literals
192− (Anything, _) ->
235+ (P.AnythingBut literals1, P.AnythingBut literals2) ->
193− Just p2
236+ Just (AnythingBut (Set.union literals1 literals2))
195− (Var _, _) ->
238+ _ ->
201− (_, Var _) ->
242+-- PATTERN COMPLEMENT
204− (Data ctor1 args1, Data ctor2 args2) ->
244+complement :: AdtDict -> P.Pattern -> [P.Pattern]
205− if ctor1 /= ctor2
245+complement adtDict nitPattern =
206− then Nothing
246+ case nitPattern of
207− else fmap (Data ctor1) (sequence (zipWith intersection args1 args2))
247+ P.Record _fields ->
209− -- TODO Assuming the fields are identical.
250+ P.Alias _name pattern ->
210− (Record _fs, Record _) ->
251+ complement adtDict pattern
213− (Literal l1, Literal l2) ->
253+ P.Var _name ->
214− if l1 == l2 then Just p1 else Nothing
254+ []
216− (AnythingBut ls, Literal l) ->
256+ P.Anything ->
217− if l `Set.member` ls then Nothing else Just p2
257+ []
219− (Literal l, AnythingBut ls) ->
259+ P.Literal lit ->
220− if l `Set.member` ls then Nothing else Just p1
260+ [AnythingBut (Set.singleton lit)]
222− (AnythingBut ls1, AnythingBut ls2) ->
262+ P.AnythingBut literals ->
223− Just (AnythingBut (Set.union ls1 ls2))
263+ map Literal (Set.toList literals)
225− _ -> Nothing
265+ P.Data ctor patterns ->
266+ complementData adtDict ctor patterns
227− checkMatch :: Region.Region -> [CanonicalPattern] -> M (Maybe (Annotation.Located Warning))
231− go :: [CanonicalPat] -> [CanonicalPattern] -> M (Maybe (Annotation.Located Warning))
233− case (unhandled, ps) of
237− (_:_, []) ->
269+complementData :: AdtDict -> Var.Canonical -> [P.Pattern] -> [P.Pattern]
238− return (Just (Annotation.A region (Inexhaustive unhandled)))
270+complementData adtDict canonicalTag patterns =
273+ lookupOtherCtors ctor adtDict
240− (_, p:ps') ->
275+ dataComplements =
241− let pp = fromPattern p
276+ Maybe.mapMaybe (maybeData canonicalTag) ctors
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'
278+ numArgs = length patterns
251−-- Util
280+ argComplements =
281+ concat (zipWith (makeArgComplements adtDict numArgs) [0..] patterns)
283+ dataComplements ++ argComplements
253−concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
255− liftM concat (mapM f list)
286+maybeData :: Var.Canonical -> (String, Module.AdtInfo) -> Maybe P.Pattern
287+maybeData (Var.Canonical home name) (tag, args) =
292+ Just (P.Data (Var.Canonical home tag) (replicate (length args) P.Anything))
258−filterMapM :: Monad m => (a -> m (Maybe b)) -> [a] -> m [b]
260− liftM catMaybes (mapM f list)
295+makeArgComplements :: AdtDict -> Int -> Int -> P.Pattern -> [P.Pattern]
296+makeArgComplements adtDict numArgs index argPattern =
299+ complement adtDict argPattern
263−maybeCons :: Maybe a -> [a] -> [a]
301+ paddedArgs pat =
264−maybeCons maybe list =
302+ replicate index P.Anything ++ pat : replicate (numArgs - index - 1) P.Anything
265− case maybe of
303+ in
266− Just x ->
304+ map (P.Data ctor . paddedArgs) complementList