@@ -19,10 +19,7 @@
19{-# LANGUAGE FlexibleContexts #-}
19{-# LANGUAGE FlexibleContexts #-}
20{-# LANGUAGE ScopedTypeVariables #-}
20{-# LANGUAGE ScopedTypeVariables #-}
22−module Language.PureScript.Linter.Exhaustive
22+module Language.PureScript.Linter.Exhaustive (checkExhaustiveModule) where
24− , checkExhaustiveModule
27import Prelude ()
24import Prelude ()
28import Prelude.Compat
25import Prelude.Compat
@@ -48,7 +45,7 @@ import Language.PureScript.Errors
49-- | There are two modes of failure for the redudancy check:
46-- | There are two modes of failure for the redudancy check:
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.
52-- 2. We didn't attempt to determine redundancy for a binder, e.g. an integer binder.
49-- 2. We didn't attempt to determine redundancy for a binder, e.g. an integer binder.
54-- We want to warn the user in the first case.
51-- We want to warn the user in the first case.
@@ -239,8 +236,8 @@ missingAlternative env mn ca uncovered
239-- it partitions that set with the new uncovered cases, until it consumes the whole set of clauses.
236-- it partitions that set with the new uncovered cases, until it consumes the whole set of clauses.
240-- Then, returns the uncovered set of case alternatives.
237-- Then, returns the uncovered set of case alternatives.
242−checkExhaustive :: forall m. (MonadWriter MultipleErrors m) => Environment -> ModuleName -> Int -> [CaseAlternative] -> m ()
239+checkExhaustive :: forall m. (MonadWriter MultipleErrors m) => Bool -> Environment -> ModuleName -> Int -> [CaseAlternative] -> m ()
243−checkExhaustive env mn numArgs cas = makeResult . first nub $ foldl' step ([initialize numArgs], (pure True, [])) cas
240+checkExhaustive hasConstraint env mn numArgs cas = makeResult . first nub $ foldl' step ([initialize numArgs], (pure True, [])) cas
245 step :: ([[Binder]], (Either RedudancyError Bool, [[Binder]])) -> CaseAlternative -> ([[Binder]], (Either RedudancyError Bool, [[Binder]]))
242 step :: ([[Binder]], (Either RedudancyError Bool, [[Binder]])) -> CaseAlternative -> ([[Binder]], (Either RedudancyError Bool, [[Binder]]))
246 step (uncovered, (nec, redundant)) ca =
243 step (uncovered, (nec, redundant)) ca =
@@ -258,13 +255,13 @@ checkExhaustive env mn numArgs cas = makeResult . first nub $ foldl' step ([init
259 makeResult :: ([[Binder]], (Either RedudancyError Bool, [[Binder]])) -> m ()
256 makeResult :: ([[Binder]], (Either RedudancyError Bool, [[Binder]])) -> m ()
260 makeResult (bss, (rr, bss')) =
257 makeResult (bss, (rr, bss')) =
261− do unless (null bss) tellExhaustive
258+ do unless (hasConstraint || null bss) tellNonExhaustive
262 unless (null bss') tellRedundant
259 unless (null bss') tellRedundant
263 case rr of
260 case rr of
264− Left Incomplete -> tellIncomplete
261+ Left Incomplete -> unless hasConstraint tellIncomplete
265 _ -> return ()
262 _ -> return ()
267− tellExhaustive = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
264+ tellNonExhaustive = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
268 tellRedundant = tell . errorMessage . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
265 tellRedundant = tell . errorMessage . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
269 tellIncomplete = tell . errorMessage $ IncompleteExhaustivityCheck
266 tellIncomplete = tell . errorMessage $ IncompleteExhaustivityCheck
@@ -279,29 +276,43 @@ checkExhaustiveDecls env mn = mapM_ onDecl
280 convert :: (Ident, NameKind, Expr) -> Declaration
277 convert :: (Ident, NameKind, Expr) -> Declaration
281 convert (name, nk, e) = ValueDeclaration name nk [] (Right e)
278 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)
283 onDecl (PositionedDeclaration pos _ dec) = censor (addHint (PositionedError pos)) (onDecl dec)
280 onDecl (PositionedDeclaration pos _ dec) = censor (addHint (PositionedError pos)) (onDecl dec)
284 onDecl _ = return ()
281 onDecl _ = return ()
286− onExpr :: Expr -> m ()
283+ onExpr :: Bool -> Expr -> m ()
287− onExpr (UnaryMinus e) = onExpr e
284+ onExpr isP (UnaryMinus e) = onExpr isP e
288− onExpr (ArrayLiteral es) = mapM_ onExpr es
285+ onExpr isP (ArrayLiteral es) = mapM_ (onExpr isP) es
289− onExpr (ObjectLiteral es) = mapM_ (onExpr . snd) es
286+ onExpr isP (ObjectLiteral es) = mapM_ (onExpr isP . snd) es
290− onExpr (TypeClassDictionaryConstructorApp _ e) = onExpr e
287+ onExpr isP (TypeClassDictionaryConstructorApp _ e) = onExpr isP e
291− onExpr (Accessor _ e) = onExpr e
288+ onExpr isP (Accessor _ e) = onExpr isP e
292− onExpr (ObjectUpdate o es) = onExpr o >> mapM_ (onExpr . snd) es
289+ onExpr isP (ObjectUpdate o es) = onExpr isP o >> mapM_ (onExpr isP . snd) es
293− onExpr (Abs _ e) = onExpr e
290+ onExpr isP (Abs _ e) = onExpr isP e
294− onExpr (App e1 e2) = onExpr e1 >> onExpr e2
291+ onExpr isP (App e1 e2) = onExpr isP e1 >> onExpr isP e2
295− onExpr (IfThenElse e1 e2 e3) = onExpr e1 >> onExpr e2 >> onExpr e3
292+ onExpr isP (IfThenElse e1 e2 e3) = onExpr isP e1 >> onExpr isP e2 >> onExpr isP e3
296− onExpr (Case es cas) = checkExhaustive env mn (length es) cas >> mapM_ onExpr es >> mapM_ onCaseAlternative cas
293+ onExpr isP (Case es cas) = checkExhaustive isP env mn (length es) cas >> mapM_ (onExpr isP) es >> mapM_ (onCaseAlternative isP) cas
297− onExpr (TypedValue _ e _) = onExpr e
294+ onExpr isP (TypedValue _ e ty) = onExpr (isP || hasPartialConstraint ty) e
298− onExpr (Let ds e) = mapM_ onDecl ds >> onExpr e
295+ onExpr isP (Let ds e) = mapM_ onDecl ds >> onExpr isP e
299− onExpr (PositionedValue pos _ e) = censor (addHint (PositionedError pos)) (onExpr e)
296+ onExpr isP (PositionedValue pos _ e) = censor (addHint (PositionedError pos)) (onExpr isP e)
300− onExpr _ = return ()
297+ onExpr _ _ = return ()
302− onCaseAlternative :: CaseAlternative -> m ()
299+ onCaseAlternative :: Bool -> CaseAlternative -> m ()
303− onCaseAlternative (CaseAlternative _ (Left es)) = mapM_ (\(e, g) -> onExpr e >> onExpr g) es
300+ onCaseAlternative isP (CaseAlternative _ (Left es)) = mapM_ (\(e, g) -> onExpr isP e >> onExpr isP g) es
304− onCaseAlternative (CaseAlternative _ (Right e)) = onExpr e
301+ onCaseAlternative isP (CaseAlternative _ (Right e)) = onExpr isP e
303+ hasPartialConstraint :: Type -> Bool
304+ hasPartialConstraint (ConstrainedType cs _) = any (go . fst) cs
306+ go :: Qualified ProperName -> Bool
308+ | qname == partialClass = True
310+ case qname `M.lookup` typeClasses env of
311+ Just ([], _, cs') -> any (go . fst) cs'
313+ partialClass :: Qualified ProperName
314+ partialClass = primName "Partial"
315+ hasPartialConstraint _ = False
307-- Exhaustivity checking over a single module
318-- Exhaustivity checking over a single module