@@ -31,7 +31,7 @@ import Data.Function (on)
3131
3232import Control.Monad (unless )
3333import Control.Applicative
34- import Control.Arrow (second )
34+ import Control.Arrow (first , second )
3535import Control.Monad.Writer.Class
3636
3737import Language.PureScript.AST.Binders
@@ -108,31 +108,30 @@ genericMerge f bsl@((s, b):bs) bsr@((s', b'):bs')
108108
109109-- |
110110-- Find the uncovered set between two binders:
111- -- the first binder is the case we are trying to cover the second one is the matching binder
111+ -- the first binder is the case we are trying to cover, the second one is the matching binder
112112--
113- missingCasesSingle :: Environment -> ModuleName -> Binder -> Binder -> [Binder ]
114- missingCasesSingle _ _ _ NullBinder = []
115- missingCasesSingle _ _ _ (VarBinder _) = []
113+ missingCasesSingle :: Environment -> ModuleName -> Binder -> Binder -> ( [Binder ], Maybe Bool )
114+ missingCasesSingle _ _ _ NullBinder = ( [] , Just True )
115+ missingCasesSingle _ _ _ (VarBinder _) = ( [] , Just True )
116116missingCasesSingle env mn (VarBinder _) b = missingCasesSingle env mn NullBinder b
117117missingCasesSingle env mn br (NamedBinder _ bl) = missingCasesSingle env mn br bl
118118missingCasesSingle env mn NullBinder cb@ (ConstructorBinder con _) =
119- concatMap (\ cp -> missingCasesSingle env mn cp cb) allPatterns
119+ ( concatMap (\ cp -> fst $ missingCasesSingle env mn cp cb) allPatterns, Just True )
120120 where
121121 allPatterns = map (\ (p, t) -> ConstructorBinder (qualifyName p mn con) (initialize $ length t))
122122 $ getConstructors env mn con
123123missingCasesSingle env mn cb@ (ConstructorBinder con bs) (ConstructorBinder con' bs')
124- | con == con' = map (ConstructorBinder con) (missingCasesMultiple env mn bs bs')
125- | otherwise = [cb]
126- missingCasesSingle _ _ NullBinder (ArrayBinder bs)
127- | null bs = []
128- | otherwise = []
124+ | con == con' = let (bs'', pr) = missingCasesMultiple env mn bs bs' in (map (ConstructorBinder con) bs'', pr)
125+ | otherwise = ([cb], Just False )
129126missingCasesSingle env mn NullBinder (ObjectBinder bs) =
130- map (ObjectBinder . zip (map fst bs)) allMisses
127+ ( map (ObjectBinder . zip (map fst bs)) allMisses, pr)
131128 where
132- allMisses = missingCasesMultiple env mn (initialize $ length bs) (map snd bs)
129+ ( allMisses, pr) = missingCasesMultiple env mn (initialize $ length bs) (map snd bs)
133130missingCasesSingle env mn (ObjectBinder bs) (ObjectBinder bs') =
134- map (ObjectBinder . zip sortedNames) $ uncurry (missingCasesMultiple env mn) ( unzip binders )
131+ ( map (ObjectBinder . zip sortedNames) allMisses, pr )
135132 where
133+ (allMisses, pr) = uncurry (missingCasesMultiple env mn) (unzip binders)
134+
136135 sortNames = sortBy (compare `on` fst )
137136
138137 (sbs, sbs') = (sortNames bs, sortNames bs')
@@ -146,12 +145,12 @@ missingCasesSingle env mn (ObjectBinder bs) (ObjectBinder bs') =
146145 compBS e s b b' = (s, compB e b b')
147146
148147 (sortedNames, binders) = unzip $ genericMerge (compBS NullBinder ) sbs sbs'
149- missingCasesSingle _ _ NullBinder (BooleanBinder b) = [BooleanBinder $ not b]
148+ missingCasesSingle _ _ NullBinder (BooleanBinder b) = ( [BooleanBinder $ not b], Just True )
150149missingCasesSingle _ _ (BooleanBinder bl) (BooleanBinder br)
151- | bl == br = []
152- | otherwise = [BooleanBinder bl]
150+ | bl == br = ( [] , Just True )
151+ | otherwise = ( [BooleanBinder bl], Just False )
153152missingCasesSingle env mn b (PositionedBinder _ _ cb) = missingCasesSingle env mn b cb
154- missingCasesSingle _ _ b _ = [b]
153+ missingCasesSingle _ _ b _ = ( [b], Nothing )
155154
156155-- |
157156-- Returns the uncovered set of binders
@@ -179,15 +178,14 @@ missingCasesSingle _ _ b _ = [b]
179178-- redundant or not, but uncovered at least. If we use `y` instead, we'll need to have a redundancy checker
180179-- (which ought to be available soon), or increase the complexity of the algorithm.
181180--
182- missingCasesMultiple :: Environment -> ModuleName -> [Binder ] -> [Binder ] -> [[Binder ]]
181+ missingCasesMultiple :: Environment -> ModuleName -> [Binder ] -> [Binder ] -> ( [[Binder ]], Maybe Bool )
183182missingCasesMultiple env mn = go
184183 where
185- go [] _ = []
186- go (x: xs) (y: ys)
187- | null miss = map (x : ) (go xs ys)
188- | otherwise = map (: xs) miss ++ map (x : ) (go xs ys)
184+ go [] [] = ([] , pure True )
185+ go (x: xs) (y: ys) = (map (: xs) miss1 ++ map (x : ) miss2, liftA2 (&&) pr1 pr2)
189186 where
190- miss = missingCasesSingle env mn x y
187+ (miss1, pr1) = missingCasesSingle env mn x y
188+ (miss2, pr2) = go xs ys
191189 go _ _ = error " Argument lengths did not match in missingCasesMultiple."
192190
193191-- |
@@ -214,13 +212,15 @@ isExhaustiveGuard (Right _) = True
214212-- |
215213-- Returns the uncovered set of case alternatives
216214--
217- missingCases :: Environment -> ModuleName -> [Binder ] -> CaseAlternative -> [[Binder ]]
215+ missingCases :: Environment -> ModuleName -> [Binder ] -> CaseAlternative -> ( [[Binder ]], Maybe Bool )
218216missingCases env mn uncovered ca = missingCasesMultiple env mn uncovered (caseAlternativeBinders ca)
219217
220- missingAlternative :: Environment -> ModuleName -> CaseAlternative -> [Binder ] -> [[Binder ]]
218+ missingAlternative :: Environment -> ModuleName -> CaseAlternative -> [Binder ] -> ( [[Binder ]], Maybe Bool )
221219missingAlternative env mn ca uncovered
222- | isExhaustiveGuard (caseAlternativeResult ca) = missingCases env mn uncovered ca
223- | otherwise = [uncovered]
220+ | isExhaustiveGuard (caseAlternativeResult ca) = mcases
221+ | otherwise = ([uncovered], snd mcases)
222+ where
223+ mcases = missingCases env mn uncovered ca
224224
225225-- |
226226-- Main exhaustivity checking function
@@ -229,20 +229,29 @@ missingAlternative env mn ca uncovered
229229-- Then, returns the uncovered set of case alternatives.
230230--
231231checkExhaustive :: forall m . (MonadWriter MultipleErrors m ) => Environment -> ModuleName -> [CaseAlternative ] -> m ()
232- checkExhaustive env mn cas = makeResult . nub $ foldl' step [initial] cas
232+ checkExhaustive env mn cas = makeResult . first nub $ foldl' step ( [initial], ( pure True , [] )) cas
233233 where
234- step :: [[Binder ]] -> CaseAlternative -> [[Binder ]]
235- step uncovered ca = concatMap (missingAlternative env mn ca) uncovered
234+ step :: ([[Binder ]], (Maybe Bool , [[Binder ]])) -> CaseAlternative -> ([[Binder ]], (Maybe Bool , [[Binder ]]))
235+ step (uncovered, (nec, redundant)) ca =
236+ let (missed, pr) = unzip (map (missingAlternative env mn ca) uncovered)
237+ cond = or <$> sequenceA pr
238+ in (concat missed, (liftA2 (&&) cond nec,
239+ if fromMaybe True cond then redundant else caseAlternativeBinders ca : redundant))
240+ where
241+ sequenceA = foldr (liftA2 (:) ) (pure [] )
236242
237243 initial :: [Binder ]
238244 initial = initialize numArgs
239245 where
240246 numArgs = length . caseAlternativeBinders . head $ cas
241247
242- makeResult :: [[Binder ]] -> m ()
243- makeResult bss = unless (null bss) tellWarning
248+ makeResult :: ([[Binder ]], (Maybe Bool , [[Binder ]])) -> m ()
249+ makeResult (bss, (_, bss')) =
250+ do unless (null bss) tellExhaustive
251+ unless (null bss') tellRedundant
244252 where
245- tellWarning = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
253+ tellExhaustive = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
254+ tellRedundant = tell . errorMessage . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
246255
247256-- |
248257-- Exhaustivity checking over a list of declarations
0 commit comments