1919{-# LANGUAGE FlexibleContexts #-}
2020{-# LANGUAGE ScopedTypeVariables #-}
2121
22- module Language.PureScript.Linter.Exhaustive
23- ( checkExhaustive
24- , checkExhaustiveModule
25- ) where
22+ module Language.PureScript.Linter.Exhaustive (checkExhaustiveModule ) where
2623
2724import Prelude ()
2825import Prelude.Compat
@@ -48,7 +45,7 @@ import Language.PureScript.Errors
4845
4946-- | There are two modes of failure for the redudancy check:
5047--
51- -- 1. Exhaustivity was incomeplete due to too many cases, so we couldn't determine redundancy.
48+ -- 1. Exhaustivity was incomplete due to too many cases, so we couldn't determine redundancy.
5249-- 2. We didn't attempt to determine redundancy for a binder, e.g. an integer binder.
5350--
5451-- We want to warn the user in the first case.
@@ -239,8 +236,8 @@ missingAlternative env mn ca uncovered
239236-- it partitions that set with the new uncovered cases, until it consumes the whole set of clauses.
240237-- Then, returns the uncovered set of case alternatives.
241238--
242- checkExhaustive :: forall m . (MonadWriter MultipleErrors m ) => Environment -> ModuleName -> Int -> [CaseAlternative ] -> m ()
243- checkExhaustive env mn numArgs cas = makeResult . first nub $ foldl' step ([initialize numArgs], (pure True , [] )) cas
239+ checkExhaustive :: forall m . (MonadWriter MultipleErrors m ) => Bool -> Environment -> ModuleName -> Int -> [CaseAlternative ] -> m ()
240+ checkExhaustive hasConstraint env mn numArgs cas = makeResult . first nub $ foldl' step ([initialize numArgs], (pure True , [] )) cas
244241 where
245242 step :: ([[Binder ]], (Either RedudancyError Bool , [[Binder ]])) -> CaseAlternative -> ([[Binder ]], (Either RedudancyError Bool , [[Binder ]]))
246243 step (uncovered, (nec, redundant)) ca =
@@ -258,13 +255,13 @@ checkExhaustive env mn numArgs cas = makeResult . first nub $ foldl' step ([init
258255
259256 makeResult :: ([[Binder ]], (Either RedudancyError Bool , [[Binder ]])) -> m ()
260257 makeResult (bss, (rr, bss')) =
261- do unless (null bss) tellExhaustive
258+ do unless (hasConstraint || null bss) tellNonExhaustive
262259 unless (null bss') tellRedundant
263260 case rr of
264- Left Incomplete -> tellIncomplete
261+ Left Incomplete -> unless hasConstraint tellIncomplete
265262 _ -> return ()
266263 where
267- tellExhaustive = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
264+ tellNonExhaustive = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
268265 tellRedundant = tell . errorMessage . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
269266 tellIncomplete = tell . errorMessage $ IncompleteExhaustivityCheck
270267
@@ -279,29 +276,43 @@ checkExhaustiveDecls env mn = mapM_ onDecl
279276 where
280277 convert :: (Ident , NameKind , Expr ) -> Declaration
281278 convert (name, nk, e) = ValueDeclaration name nk [] (Right e)
282- onDecl (ValueDeclaration name _ _ (Right e)) = censor (addHint (ErrorInValueDeclaration name)) (onExpr e)
279+ onDecl (ValueDeclaration name _ _ (Right e)) = censor (addHint (ErrorInValueDeclaration name)) (onExpr False e)
283280 onDecl (PositionedDeclaration pos _ dec) = censor (addHint (PositionedError pos)) (onDecl dec)
284281 onDecl _ = return ()
285282
286- onExpr :: Expr -> m ()
287- onExpr (UnaryMinus e) = onExpr e
288- onExpr (ArrayLiteral es) = mapM_ onExpr es
289- onExpr (ObjectLiteral es) = mapM_ (onExpr . snd ) es
290- onExpr (TypeClassDictionaryConstructorApp _ e) = onExpr e
291- onExpr (Accessor _ e) = onExpr e
292- onExpr (ObjectUpdate o es) = onExpr o >> mapM_ (onExpr . snd ) es
293- onExpr (Abs _ e) = onExpr e
294- onExpr (App e1 e2) = onExpr e1 >> onExpr e2
295- onExpr (IfThenElse e1 e2 e3) = onExpr e1 >> onExpr e2 >> onExpr e3
296- onExpr (Case es cas) = checkExhaustive env mn (length es) cas >> mapM_ onExpr es >> mapM_ onCaseAlternative cas
297- onExpr (TypedValue _ e _) = onExpr e
298- onExpr (Let ds e) = mapM_ onDecl ds >> onExpr e
299- onExpr (PositionedValue pos _ e) = censor (addHint (PositionedError pos)) (onExpr e)
300- onExpr _ = return ()
301-
302- onCaseAlternative :: CaseAlternative -> m ()
303- onCaseAlternative (CaseAlternative _ (Left es)) = mapM_ (\ (e, g) -> onExpr e >> onExpr g) es
304- onCaseAlternative (CaseAlternative _ (Right e)) = onExpr e
283+ onExpr :: Bool -> Expr -> m ()
284+ onExpr isP (UnaryMinus e) = onExpr isP e
285+ onExpr isP (ArrayLiteral es) = mapM_ (onExpr isP) es
286+ onExpr isP (ObjectLiteral es) = mapM_ (onExpr isP . snd ) es
287+ onExpr isP (TypeClassDictionaryConstructorApp _ e) = onExpr isP e
288+ onExpr isP (Accessor _ e) = onExpr isP e
289+ onExpr isP (ObjectUpdate o es) = onExpr isP o >> mapM_ (onExpr isP . snd ) es
290+ onExpr isP (Abs _ e) = onExpr isP e
291+ onExpr isP (App e1 e2) = onExpr isP e1 >> onExpr isP e2
292+ onExpr isP (IfThenElse e1 e2 e3) = onExpr isP e1 >> onExpr isP e2 >> onExpr isP e3
293+ onExpr isP (Case es cas) = checkExhaustive isP env mn (length es) cas >> mapM_ (onExpr isP) es >> mapM_ (onCaseAlternative isP) cas
294+ onExpr isP (TypedValue _ e ty) = onExpr (isP || hasPartialConstraint ty) e
295+ onExpr isP (Let ds e) = mapM_ onDecl ds >> onExpr isP e
296+ onExpr isP (PositionedValue pos _ e) = censor (addHint (PositionedError pos)) (onExpr isP e)
297+ onExpr _ _ = return ()
298+
299+ onCaseAlternative :: Bool -> CaseAlternative -> m ()
300+ onCaseAlternative isP (CaseAlternative _ (Left es)) = mapM_ (\ (e, g) -> onExpr isP e >> onExpr isP g) es
301+ onCaseAlternative isP (CaseAlternative _ (Right e)) = onExpr isP e
302+
303+ hasPartialConstraint :: Type -> Bool
304+ hasPartialConstraint (ConstrainedType cs _) = any (go . fst ) cs
305+ where
306+ go :: Qualified ProperName -> Bool
307+ go qname
308+ | qname == partialClass = True
309+ | otherwise =
310+ case qname `M.lookup` typeClasses env of
311+ Just ([] , _, cs') -> any (go . fst ) cs'
312+ _ -> False
313+ partialClass :: Qualified ProperName
314+ partialClass = primName " Partial"
315+ hasPartialConstraint _ = False
305316
306317-- |
307318-- Exhaustivity checking over a single module
0 commit comments