{-# LANGUAGE CPP, RankNTypes, GADTs #-}
#if __GLASGOW_HASKELL__ >= 800
#define TheExc TypeError
#else
#define TheExc ErrorCall
#endif
module Test.ShouldNotTypecheck (shouldNotTypecheck) where
import Control.DeepSeq (force, NFData)
import Control.Exception (evaluate, try, throwIO, TheExc(..))
import Data.List (isSuffixOf)
import Test.HUnit.Lang (Assertion, assertFailure)
isSubsequenceOf :: (Eq a) => [a] -> [a] -> Bool
isSubsequenceOf :: forall a. Eq a => [a] -> [a] -> Bool
isSubsequenceOf [] [a]
_ = Bool
True
isSubsequenceOf [a]
_ [] = Bool
False
isSubsequenceOf a :: [a]
a@(a
x:[a]
a') (a
y:[a]
b) | a
x a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
y = [a] -> [a] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSubsequenceOf [a]
a' [a]
b
| Bool
otherwise = [a] -> [a] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isSubsequenceOf [a]
a [a]
b
#if __GLASGOW_HASKELL__ >= 800
shouldNotTypecheck :: NFData a => (() ~ () => a) -> Assertion
#else
shouldNotTypecheck :: NFData a => a -> Assertion
#endif
shouldNotTypecheck :: forall a. NFData a => ((() ~ ()) => a) -> Assertion
shouldNotTypecheck (() ~ ()) => a
a = do
Either TypeError a
result <- IO a -> IO (Either TypeError a)
forall e a. Exception e => IO a -> IO (Either e a)
try (a -> IO a
forall a. a -> IO a
evaluate (a -> IO a) -> a -> IO a
forall a b. (a -> b) -> a -> b
$ a -> a
forall a. NFData a => a -> a
force a
(() ~ ()) => a
a)
case Either TypeError a
result of
Right a
_ -> String -> Assertion
forall a. HasCallStack => String -> IO a
assertFailure String
"Expected expression to not compile but it did compile"
Left e :: TypeError
e@(TheExc msg) -> case isSuffixOf "(deferred type error)" msg of
True -> case isSubsequenceOf "No instance for" msg && isSubsequenceOf "NFData" msg of
True -> assertFailure $ "Make sure the expression has an NFData instance! See docs at https://github.com/CRogers/should-not-typecheck#nfdata-a-constraint. Full error:\n" ++ msg
False -> return ()
False -> throwIO e