packages feed

variadic-0.0.0.0: test/Test/ShouldNotCompile.hs

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
module Test.ShouldNotCompile where

import Control.Monad (when)
import Data.Maybe (fromMaybe)
import System.Environment (lookupEnv)
import System.Exit (ExitCode(ExitSuccess))
import System.Process (readProcessWithExitCode)
import Test.Hspec (HasCallStack, expectationFailure, shouldBe)
import qualified Data.List as List

-- | Test support for asserting that expressions should not compile.
--
-- Attempts to compile the supplied @moduleName@ in @testdata/${moduleName}.hs@ and
-- asserts that it both fails to compile and reports error messages that match
-- the contents of @testdata/${moduleName}.txt@.
--
-- You may be tempted to use @-fdefer-type-errors@ and/or the
-- @should-not-typecheck@ package; however, this doesn't work for custom type errors.
-- See: https://gitlab.haskell.org/ghc/ghc/-/issues/18310
shouldNotCompile :: (HasCallStack) => String -> IO ()
shouldNotCompile moduleName = do
  stackExe <- fromMaybe "stack" <$> lookupEnv "STACK_EXE"
  (exitCode, _out, err) <- readProcessWithExitCode
    stackExe
    [ "ghc"
    , "--"
    , "testdata/" <> moduleName <> ".hs"
    , "-fno-code"
    ]
    ""
  when (exitCode == ExitSuccess) do
    expectationFailure $ "Unexpected compilation success for module " <> moduleName
  let actual = CompileError $ unlines $ dropWhile skipLine $ lines err
  expected <- fmap CompileError $ readFile $ "testdata/" <> moduleName <> ".txt"
  actual `shouldBe` expected
  where
  skipLine line =
    "Stack has not been tested with" `List.isPrefixOf` line
      || null line

newtype CompileError = CompileError String
  deriving newtype (Eq)

instance Show CompileError where
  show (CompileError s) = s