@@ -29,6 +29,9 @@ import qualified Data.Map as M
29import Data.Maybe (fromMaybe)
29import Data.Maybe (fromMaybe)
30import Data.List (foldl', sortBy, nub)
30import Data.List (foldl', sortBy, nub)
31import Data.Function (on)
31import Data.Function (on)
32+#if __GLASGOW_HASKELL__ < 710
33+import Data.Traversable (sequenceA)
33import Control.Monad (unless)
36import Control.Monad (unless)
34import Control.Applicative
37import Control.Applicative
@@ -45,6 +48,14 @@ import Language.PureScript.Errors
46import Language.PureScript.AST.Traversals (everywhereOnValuesTopDownM)
49import Language.PureScript.AST.Traversals (everywhereOnValuesTopDownM)
51+-- | There are two modes of failure for the redudancy check:
53+-- 1. Exhaustivity was incomeplete due to too many cases, so we couldn't determine redundancy.
54+-- 2. We didn't attempt to determine redundancy for a binder, e.g. an integer binder.
56+-- We want to warn the user in the first case.
57+data RedudancyError = Incomplete | Unknown
49-- Qualifies a propername from a given qualified propername and a default module name
60-- Qualifies a propername from a given qualified propername and a default module name
@@ -59,20 +70,20 @@ qualifyName n defmn qn = Qualified (Just mn) n
59-- where: - ProperName is the name of the constructor (for example, "Nothing" in Maybe)
70-- where: - ProperName is the name of the constructor (for example, "Nothing" in Maybe)
60-- - [Type] is the list of arguments, if it has (for example, "Just" has [TypeVar "a"])
71-- - [Type] is the list of arguments, if it has (for example, "Just" has [TypeVar "a"])
62−getConstructors :: Environment -> ModuleName -> (Qualified ProperName) -> [(ProperName, [Type])]
73+getConstructors :: Environment -> ModuleName -> Qualified ProperName -> [(ProperName, [Type])]
63getConstructors env defmn n = extractConstructors lnte
74getConstructors env defmn n = extractConstructors lnte
65 qpn :: Qualified ProperName
76 qpn :: Qualified ProperName
66 qpn = getConsDataName n
77 qpn = getConsDataName n
68− getConsDataName :: (Qualified ProperName) -> (Qualified ProperName)
79+ getConsDataName :: Qualified ProperName -> Qualified ProperName
69 getConsDataName con = qualifyName nm defmn con
80 getConsDataName con = qualifyName nm defmn con
71 nm = case getConsInfo con of
82 nm = case getConsInfo con of
72 Nothing -> error $ "Constructor " ++ showQualified runProperName con ++ " not in the scope of the current environment in getConsDataName."
83 Nothing -> error $ "Constructor " ++ showQualified runProperName con ++ " not in the scope of the current environment in getConsDataName."
73 Just (_, pm, _, _) -> pm
84 Just (_, pm, _, _) -> pm
75− getConsInfo :: (Qualified ProperName) -> Maybe (DataDeclType, ProperName, Type, [Ident])
86+ getConsInfo :: Qualified ProperName -> Maybe (DataDeclType, ProperName, Type, [Ident])
76 getConsInfo con = M.lookup con dce
87 getConsInfo con = M.lookup con dce
78 dce :: M.Map (Qualified ProperName) (DataDeclType, ProperName, Type, [Ident])
89 dce :: M.Map (Qualified ProperName) (DataDeclType, ProperName, Type, [Ident])
@@ -103,27 +114,27 @@ genericMerge _ [] [] = []
103genericMerge f bs [] = map (\(s, b) -> f s (Just b) Nothing) bs
114genericMerge f bs [] = map (\(s, b) -> f s (Just b) Nothing) bs
104genericMerge f [] bs = map (\(s, b) -> f s Nothing (Just b)) bs
115genericMerge f [] bs = map (\(s, b) -> f s Nothing (Just b)) bs
105genericMerge f bsl@((s, b):bs) bsr@((s', b'):bs')
116genericMerge f bsl@((s, b):bs) bsr@((s', b'):bs')
106− | s < s' = (f s (Just b) Nothing) : genericMerge f bs bsr
117+ | s < s' = f s (Just b) Nothing : genericMerge f bs bsr
107− | s > s' = (f s' Nothing (Just b')) : genericMerge f bsl bs'
118+ | s > s' = f s' Nothing (Just b') : genericMerge f bsl bs'
108− | otherwise = (f s (Just b) (Just b')) : genericMerge f bs bs'
119+ | otherwise = f s (Just b) (Just b') : genericMerge f bs bs'
111-- Find the uncovered set between two binders:
122-- Find the uncovered set between two binders:
112-- the first binder is the case we are trying to cover, the second one is the matching binder
123-- the first binder is the case we are trying to cover, the second one is the matching binder
114−missingCasesSingle :: Environment -> ModuleName -> Binder -> Binder -> ([Binder], Maybe Bool)
125+missingCasesSingle :: Environment -> ModuleName -> Binder -> Binder -> ([Binder], Either RedudancyError Bool)
115−missingCasesSingle _ _ _ NullBinder = ([], Just True)
126+missingCasesSingle _ _ _ NullBinder = ([], return True)
116−missingCasesSingle _ _ _ (VarBinder _) = ([], Just True)
127+missingCasesSingle _ _ _ (VarBinder _) = ([], return True)
117missingCasesSingle env mn (VarBinder _) b = missingCasesSingle env mn NullBinder b
128missingCasesSingle env mn (VarBinder _) b = missingCasesSingle env mn NullBinder b
118missingCasesSingle env mn br (NamedBinder _ bl) = missingCasesSingle env mn br bl
129missingCasesSingle env mn br (NamedBinder _ bl) = missingCasesSingle env mn br bl
119missingCasesSingle env mn NullBinder cb@(ConstructorBinder con _) =
130missingCasesSingle env mn NullBinder cb@(ConstructorBinder con _) =
120− (concatMap (\cp -> fst $ missingCasesSingle env mn cp cb) allPatterns, Just True)
131+ (concatMap (\cp -> fst $ missingCasesSingle env mn cp cb) allPatterns, return True)
122 allPatterns = map (\(p, t) -> ConstructorBinder (qualifyName p mn con) (initialize $ length t))
133 allPatterns = map (\(p, t) -> ConstructorBinder (qualifyName p mn con) (initialize $ length t))
123 $ getConstructors env mn con
134 $ getConstructors env mn con
124missingCasesSingle env mn cb@(ConstructorBinder con bs) (ConstructorBinder con' bs')
135missingCasesSingle env mn cb@(ConstructorBinder con bs) (ConstructorBinder con' bs')
125 | con == con' = let (bs'', pr) = missingCasesMultiple env mn bs bs' in (map (ConstructorBinder con) bs'', pr)
136 | con == con' = let (bs'', pr) = missingCasesMultiple env mn bs bs' in (map (ConstructorBinder con) bs'', pr)
126− | otherwise = ([cb], Just False)
137+ | otherwise = ([cb], return False)
127missingCasesSingle env mn NullBinder (ObjectBinder bs) =
138missingCasesSingle env mn NullBinder (ObjectBinder bs) =
128 (map (ObjectBinder . zip (map fst bs)) allMisses, pr)
139 (map (ObjectBinder . zip (map fst bs)) allMisses, pr)
@@ -146,12 +157,12 @@ missingCasesSingle env mn (ObjectBinder bs) (ObjectBinder bs') =
146 compBS e s b b' = (s, compB e b b')
157 compBS e s b b' = (s, compB e b b')
148 (sortedNames, binders) = unzip $ genericMerge (compBS NullBinder) sbs sbs'
159 (sortedNames, binders) = unzip $ genericMerge (compBS NullBinder) sbs sbs'
149−missingCasesSingle _ _ NullBinder (BooleanBinder b) = ([BooleanBinder $ not b], Just True)
160+missingCasesSingle _ _ NullBinder (BooleanBinder b) = ([BooleanBinder $ not b], return True)
150missingCasesSingle _ _ (BooleanBinder bl) (BooleanBinder br)
161missingCasesSingle _ _ (BooleanBinder bl) (BooleanBinder br)
151− | bl == br = ([], Just True)
162+ | bl == br = ([], return True)
152− | otherwise = ([BooleanBinder bl], Just False)
163+ | otherwise = ([BooleanBinder bl], return False)
153missingCasesSingle env mn b (PositionedBinder _ _ cb) = missingCasesSingle env mn b cb
164missingCasesSingle env mn b (PositionedBinder _ _ cb) = missingCasesSingle env mn b cb
154−missingCasesSingle _ _ b _ = ([b], Nothing)
165+missingCasesSingle _ _ b _ = ([b], Left Unknown)
157-- Returns the uncovered set of binders
168-- Returns the uncovered set of binders
@@ -179,7 +190,7 @@ missingCasesSingle _ _ b _ = ([b], Nothing)
179-- redundant or not, but uncovered at least. If we use `y` instead, we'll need to have a redundancy checker
190-- redundant or not, but uncovered at least. If we use `y` instead, we'll need to have a redundancy checker
180-- (which ought to be available soon), or increase the complexity of the algorithm.
191-- (which ought to be available soon), or increase the complexity of the algorithm.
182−missingCasesMultiple :: Environment -> ModuleName -> [Binder] -> [Binder] -> ([[Binder]], Maybe Bool)
193+missingCasesMultiple :: Environment -> ModuleName -> [Binder] -> [Binder] -> ([[Binder]], Either RedudancyError Bool)
183missingCasesMultiple env mn = go
194missingCasesMultiple env mn = go
185 go [] [] = ([], pure True)
196 go [] [] = ([], pure True)
@@ -213,10 +224,10 @@ isExhaustiveGuard (Right _) = True
214-- Returns the uncovered set of case alternatives
225-- Returns the uncovered set of case alternatives
216−missingCases :: Environment -> ModuleName -> [Binder] -> CaseAlternative -> ([[Binder]], Maybe Bool)
227+missingCases :: Environment -> ModuleName -> [Binder] -> CaseAlternative -> ([[Binder]], Either RedudancyError Bool)
217missingCases env mn uncovered ca = missingCasesMultiple env mn uncovered (caseAlternativeBinders ca)
228missingCases env mn uncovered ca = missingCasesMultiple env mn uncovered (caseAlternativeBinders ca)
219−missingAlternative :: Environment -> ModuleName -> CaseAlternative -> [Binder] -> ([[Binder]], Maybe Bool)
230+missingAlternative :: Environment -> ModuleName -> CaseAlternative -> [Binder] -> ([[Binder]], Either RedudancyError Bool)
220missingAlternative env mn ca uncovered
231missingAlternative env mn ca uncovered
221 | isExhaustiveGuard (caseAlternativeResult ca) = mcases
232 | isExhaustiveGuard (caseAlternativeResult ca) = mcases
222 | otherwise = ([uncovered], snd mcases)
233 | otherwise = ([uncovered], snd mcases)
@@ -232,24 +243,31 @@ missingAlternative env mn ca uncovered
232checkExhaustive :: forall m. (MonadWriter MultipleErrors m) => Environment -> ModuleName -> Int -> [CaseAlternative] -> m ()
243checkExhaustive :: forall m. (MonadWriter MultipleErrors m) => Environment -> ModuleName -> Int -> [CaseAlternative] -> m ()
233checkExhaustive env mn numArgs cas = makeResult . first nub $ foldl' step ([initialize numArgs], (pure True, [])) cas
244checkExhaustive env mn numArgs cas = makeResult . first nub $ foldl' step ([initialize numArgs], (pure True, [])) cas
235− step :: ([[Binder]], (Maybe Bool, [[Binder]])) -> CaseAlternative -> ([[Binder]], (Maybe Bool, [[Binder]]))
246+ step :: ([[Binder]], (Either RedudancyError Bool, [[Binder]])) -> CaseAlternative -> ([[Binder]], (Either RedudancyError Bool, [[Binder]]))
236 step (uncovered, (nec, redundant)) ca =
247 step (uncovered, (nec, redundant)) ca =
237 let (missed, pr) = unzip (map (missingAlternative env mn ca) uncovered)
248 let (missed, pr) = unzip (map (missingAlternative env mn ca) uncovered)
249+ (missed', approx) = splitAt 10000 (concat missed)
238− cond = or <$> sequenceA pr
250+ cond = liftA2 (&&) (or <$> sequenceA pr) nec
239− in (concat missed, (liftA2 (&&) cond nec,
251+ in (missed', ( if null approx
253+ else Left Incomplete
254+ , if either (const True) id cond
240− if fromMaybe True cond then redundant else caseAlternativeBinders ca : redundant))
256+ else caseAlternativeBinders ca : redundant
241−#if __GLASGOW_HASKELL__ < 710
257+ )
243− sequenceA = foldr (liftA2 (:)) (pure [])
246− makeResult :: ([[Binder]], (Maybe Bool, [[Binder]])) -> m ()
260+ makeResult :: ([[Binder]], (Either RedudancyError Bool, [[Binder]])) -> m ()
247− makeResult (bss, (_, bss')) =
261+ makeResult (bss, (rr, bss')) =
248 do unless (null bss) tellExhaustive
262 do unless (null bss) tellExhaustive
249 unless (null bss') tellRedundant
263 unless (null bss') tellRedundant
265+ Left Incomplete -> tellIncomplete
251 tellExhaustive = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
268 tellExhaustive = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
252 tellRedundant = tell . errorMessage . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
269 tellRedundant = tell . errorMessage . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
270+ tellIncomplete = tell . errorMessage $ IncompleteExhaustivityCheck
255-- Exhaustivity checking over a list of declarations
273-- Exhaustivity checking over a list of declarations