@@ -7,7 +7,9 @@
7-- to bottom, where in each step it has the cases already missing (uncovered),
7-- to bottom, where in each step it has the cases already missing (uncovered),
8-- and it generates the new set of missing cases.
8-- and it generates the new set of missing cases.
10−module Language.PureScript.Linter.Exhaustive (checkExhaustiveModule) where
10+module Language.PureScript.Linter.Exhaustive
11+ ( checkExhaustiveExpr
12import Prelude ()
14import Prelude ()
13import Prelude.Compat
15import Prelude.Compat
@@ -23,12 +25,15 @@ import Control.Arrow (first, second)
23import Control.Monad.Writer.Class
25import Control.Monad.Writer.Class
25import Language.PureScript.Crash
27import Language.PureScript.Crash
28+import qualified Language.PureScript.Constants as C
26import Language.PureScript.AST.Binders
29import Language.PureScript.AST.Binders
27import Language.PureScript.AST.Literals
30import Language.PureScript.AST.Literals
28import Language.PureScript.AST.Declarations
31import Language.PureScript.AST.Declarations
29import Language.PureScript.Environment
32import Language.PureScript.Environment
30import Language.PureScript.Names as P
33import Language.PureScript.Names as P
31import Language.PureScript.Kinds
34import Language.PureScript.Kinds
35+import Language.PureScript.Pretty.Values (prettyPrintBinderAtom)
36+import Language.PureScript.Traversals
32import Language.PureScript.Types as P
37import Language.PureScript.Types as P
33import Language.PureScript.Errors
38import Language.PureScript.Errors
@@ -179,12 +184,11 @@ missingCasesSingle _ _ b _ = ([b], Left Unknown)
179missingCasesMultiple :: Environment -> ModuleName -> [Binder] -> [Binder] -> ([[Binder]], Either RedundancyError Bool)
184missingCasesMultiple :: Environment -> ModuleName -> [Binder] -> [Binder] -> ([[Binder]], Either RedundancyError Bool)
180missingCasesMultiple env mn = go
185missingCasesMultiple env mn = go
182− go [] [] = ([], pure True)
183 go (x:xs) (y:ys) = (map (: xs) miss1 ++ map (x :) miss2, liftA2 (&&) pr1 pr2)
187 go (x:xs) (y:ys) = (map (: xs) miss1 ++ map (x :) miss2, liftA2 (&&) pr1 pr2)
185 (miss1, pr1) = missingCasesSingle env mn x y
189 (miss1, pr1) = missingCasesSingle env mn x y
186 (miss2, pr2) = go xs ys
190 (miss2, pr2) = go xs ys
187− go _ _ = internalError "Argument lengths did not match in missingCasesMultiple."
191+ go _ _ = ([], pure True)
190-- Guard handling
194-- Guard handling
@@ -229,8 +233,16 @@ missingAlternative env mn ca uncovered
229-- it partitions that set with the new uncovered cases, until it consumes the whole set of clauses.
233-- it partitions that set with the new uncovered cases, until it consumes the whole set of clauses.
230-- Then, returns the uncovered set of case alternatives.
234-- Then, returns the uncovered set of case alternatives.
232−checkExhaustive :: forall m. (MonadWriter MultipleErrors m) => Bool -> Environment -> ModuleName -> Int -> [CaseAlternative] -> m ()
238+ . (MonadWriter MultipleErrors m)
242+ -> [CaseAlternative]
233−checkExhaustive hasConstraint env mn numArgs cas = makeResult . first nub $ foldl' step ([initialize numArgs], (pure True, [])) cas
245+checkExhaustive env mn numArgs cas expr = makeResult . first nub $ foldl' step ([initialize numArgs], (pure True, [])) cas
235 step :: ([[Binder]], (Either RedundancyError Bool, [[Binder]])) -> CaseAlternative -> ([[Binder]], (Either RedundancyError Bool, [[Binder]]))
247 step :: ([[Binder]], (Either RedundancyError Bool, [[Binder]])) -> CaseAlternative -> ([[Binder]], (Either RedundancyError Bool, [[Binder]]))
236 step (uncovered, (nec, redundant)) ca =
248 step (uncovered, (nec, redundant)) ca =
@@ -246,69 +258,83 @@ checkExhaustive hasConstraint env mn numArgs cas = makeResult . first nub $ fold
249− makeResult :: ([[Binder]], (Either RedundancyError Bool, [[Binder]])) -> m ()
261+ makeResult :: ([[Binder]], (Either RedundancyError Bool, [[Binder]])) -> m Expr
250 makeResult (bss, (rr, bss')) =
262 makeResult (bss, (rr, bss')) =
251− do unless (hasConstraint || null bss) tellNonExhaustive
252− unless (null bss') tellRedundant
263+ do unless (null bss') tellRedundant
253 case rr of
264 case rr of
254− Left Incomplete -> unless hasConstraint tellIncomplete
265+ Left Incomplete -> tellIncomplete
255 _ -> return ()
266 _ -> return ()
269+ else return (addPartialConstraint (second null (splitAt 5 bss)) expr)
257− tellNonExhaustive = tell . errorMessage . uncurry NotExhaustivePattern . second null . splitAt 5 $ bss
258− tellRedundant = tell . errorMessage . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
271+ tellRedundant = tell . errorMessage . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
259− tellIncomplete = tell . errorMessage $ IncompleteExhaustivityCheck
272+ tellIncomplete = tell . errorMessage $ IncompleteExhaustivityCheck
261−-- |
274+ -- | We add a Partial constraint by adding a call to the following identity function:
262−-- Exhaustivity checking over a list of declarations
264−checkExhaustiveDecls :: forall m. MonadWriter MultipleErrors m => Environment -> ModuleName -> [Declaration] -> m ()
276+ -- partial :: forall a. Partial => a -> a
265−checkExhaustiveDecls env mn = mapM_ onDecl
277+ --
266− where
278+ -- The binder information is provided so that it can be embedded in the constraint,
267− onDecl :: Declaration -> m ()
279+ -- and then included in the error message.
268− onDecl (BindingGroupDeclaration bs) = mapM_ (onDecl . convert) bs
280+ addPartialConstraint :: ([[Binder]], Bool) -> Expr -> Expr
269− where
281+ addPartialConstraint (bss, complete) e =
270− convert :: (Ident, NameKind, Expr) -> Declaration
282+ Let [ partial ] (App (Var (Qualified Nothing (Ident C.__unused))) e)
271− convert (name, nk, e) = ValueDeclaration name nk [] (Right e)
272− onDecl (ValueDeclaration name _ _ (Right e)) = censor (addHint (ErrorInValueDeclaration name)) (onExpr False e)
273− onDecl (PositionedDeclaration pos _ dec) = censor (addHint (PositionedError pos)) (onDecl dec)
274− onDecl _ = return ()
276− onExpr :: Bool -> Expr -> m ()
277− onExpr isP (UnaryMinus e) = onExpr isP e
278− onExpr isP (Literal (ArrayLiteral es)) = mapM_ (onExpr isP) es
279− onExpr isP (Literal (ObjectLiteral es)) = mapM_ (onExpr isP . snd) es
280− onExpr isP (TypeClassDictionaryConstructorApp _ e) = onExpr isP e
281− onExpr isP (Accessor _ e) = onExpr isP e
282− onExpr isP (ObjectUpdate o es) = onExpr isP o >> mapM_ (onExpr isP . snd) es
283− onExpr isP (Abs _ e) = onExpr isP e
284− onExpr isP (App e1 e2) = onExpr isP e1 >> onExpr isP e2
285− onExpr isP (IfThenElse e1 e2 e3) = onExpr isP e1 >> onExpr isP e2 >> onExpr isP e3
286− onExpr isP (Case es cas) = checkExhaustive isP env mn (length es) cas >> mapM_ (onExpr isP) es >> mapM_ (onCaseAlternative isP) cas
287− onExpr isP (TypedValue _ e ty) = onExpr (isP || hasPartialConstraint ty) e
288− onExpr isP (Let ds e) = mapM_ onDecl ds >> onExpr isP e
289− onExpr isP (PositionedValue pos _ e) = censor (addHint (PositionedError pos)) (onExpr isP e)
290− onExpr _ _ = return ()
292− onCaseAlternative :: Bool -> CaseAlternative -> m ()
293− onCaseAlternative isP (CaseAlternative _ (Left es)) = mapM_ (\(e, g) -> onExpr isP e >> onExpr isP g) es
294− onCaseAlternative isP (CaseAlternative _ (Right e)) = onExpr isP e
296− hasPartialConstraint :: Type -> Bool
297− hasPartialConstraint (ConstrainedType cs _) = any (go . fst) cs
299− go :: Qualified (ProperName 'ClassName) -> Bool
284+ partial :: Declaration
300− go qname
285+ partial = ValueDeclaration (Ident C.__unused)
301− | qname == partialClass = True
286+ Private
302− | otherwise =
287+ []
303− case qname `M.lookup` typeClasses env of
288+ (Right (TypedValue True (Abs (Left (Ident "x"))
304− Just ([], _, cs') -> any (go . fst) cs'
289+ (Var (Qualified Nothing (Ident "x"))))
306− partialClass :: Qualified (ProperName 'ClassName)
291+
307− partialClass = primName "Partial"
292+ ty :: Type
308− hasPartialConstraint _ = False
293+ ty = ForAll "a" (ConstrainedType [ Constraint (Qualified (Just (ModuleName [ProperName C.prim]))
294+ (ProperName "Partial"))
296+ (Just (PartialConstraintData (map (map prettyPrintBinderAtom) bss) complete))
298+ (TypeApp (TypeApp tyFunction (TypeVar "a"))
311−-- Exhaustivity checking over a single module
303+-- Exhaustivity checking
313−checkExhaustiveModule :: forall m. MonadWriter MultipleErrors m => Environment -> Module -> m ()
307+ . MonadWriter MultipleErrors m
314−checkExhaustiveModule env (Module _ _ mn ds _) = censor (addHint (ErrorInModule mn)) $ checkExhaustiveDecls env mn ds
308+ => Environment
312+checkExhaustiveExpr env mn = onExpr
314+ onDecl :: Declaration -> m Declaration
315+ onDecl (BindingGroupDeclaration bs) = BindingGroupDeclaration <$> mapM (thirdM onExpr) bs
316+ onDecl (ValueDeclaration name x y (Right e)) = ValueDeclaration name x y . Right <$> censor (addHint (ErrorInValueDeclaration name)) (onExpr e)
317+ onDecl (PositionedDeclaration pos x dec) = PositionedDeclaration pos x <$> censor (addHint (PositionedError pos)) (onDecl dec)
318+ onDecl decl = return decl
320+ onExpr :: Expr -> m Expr
321+ onExpr (UnaryMinus e) = UnaryMinus <$> onExpr e
322+ onExpr (Literal (ArrayLiteral es)) = Literal . ArrayLiteral <$> mapM onExpr es
323+ onExpr (Literal (ObjectLiteral es)) = Literal . ObjectLiteral <$> mapM (sndM onExpr) es
324+ onExpr (TypeClassDictionaryConstructorApp x e) = TypeClassDictionaryConstructorApp x <$> onExpr e
325+ onExpr (Accessor x e) = Accessor x <$> onExpr e
326+ onExpr (ObjectUpdate o es) = ObjectUpdate <$> onExpr o <*> mapM (sndM onExpr) es
327+ onExpr (Abs x e) = Abs x <$> onExpr e
328+ onExpr (App e1 e2) = App <$> onExpr e1 <*> onExpr e2
329+ onExpr (IfThenElse e1 e2 e3) = IfThenElse <$> onExpr e1 <*> onExpr e2 <*> onExpr e3
330+ onExpr (Case es cas) = do
331+ case' <- Case <$> mapM onExpr es <*> mapM onCaseAlternative cas
332+ checkExhaustive env mn (length es) cas case'
333+ onExpr (TypedValue x e y) = TypedValue x <$> onExpr e <*> pure y
334+ onExpr (Let ds e) = Let <$> mapM onDecl ds <*> onExpr e
335+ onExpr (PositionedValue pos x e) = PositionedValue pos x <$> censor (addHint (PositionedError pos)) (onExpr e)
336+ onExpr expr = return expr
338+ onCaseAlternative :: CaseAlternative -> m CaseAlternative
339+ onCaseAlternative (CaseAlternative x (Left es)) = CaseAlternative x . Left <$> mapM (\(e, g) -> (,) <$> onExpr e <*> onExpr g) es
340+ onCaseAlternative (CaseAlternative x (Right e)) = CaseAlternative x . Right <$> onExpr e