ghc-hie-0.0.5: test/GHC/Iface/Ext/BinarySpec.hs
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE BlockArguments #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module GHC.Iface.Ext.BinarySpec (spec) where
import Test.Hspec
import Control.Concurrent (readMVar)
import Data.Maybe
import Data.String
import Data.Functor
import Data.Foldable
import Data.Map qualified as Map
import Data.ByteString.Char8 qualified as B
import System.Process
import System.FilePath
import System.IO.Temp
import System.Environment.Blank (unsetEnv)
import GHC.Unit.Types
import GHC.Types.SrcLoc
import GHC.Types.Name
import GHC.Types.Name.Cache
import GHC.Unit.Module.Env
import Language.Haskell.Syntax.Module.Name
import GHC.Iface.Ext.Binary
import GHC.Iface.Ext.Types
import GHC.Iface.Ext.Utils ()
import GHC.Iface.Ext.Debug ()
run :: CreateProcess -> IO ()
run p = withCreateProcess p \ _ _ _ -> void . waitForProcess
deriving instance Show unit => Show (GenModule unit)
deriving newtype instance IsString ModuleName
deriving newtype instance IsString UnitId
instance IsString Unit where
fromString = RealUnit . Definite . fromString
withHieFile :: String -> (FilePath -> IO b) -> IO b
withHieFile ghc action = do
withSystemTempDirectory "hspec" \ dir -> do
createHieFile ghc dir "Foo.hs" [
"module Foo where"
, "foo :: Int"
, "foo = 23"
]
action $ dir </> "Foo.hie"
createHieFile :: FilePath -> FilePath -> FilePath -> [String] -> IO ()
createHieFile ghc dir name contents = do
writeFile (dir </> name) $ unlines contents
unsetEnv "GHC_ENVIRONMENT"
run (proc ghc ["-v0", "-fwrite-ide-info", name]) { cwd = Just dir }
supported :: [(String, Integer)]
supported = [
("9.8.1", 9081)
, ("9.8.4", 9084)
, ("9.10.1", 9101)
, ("9.10.2", 9102)
, ("9.10.3", 9103)
, ("9.12.1", 9121)
, ("9.12.2", 9122)
, ("9.12.4", 9124)
, ("9.14.1", 9141)
]
spec :: Spec
spec = do
describe "readHieFile" do
it "rejects HIE-files created with GHC 9.4.8" do
withHieFile "ghc-9.4.8" \ hieFile -> do
extractSourceFileName hieFile `shouldReturn` "Foo.hs"
let
message =
"Unsupported HIE version 9048 for file "
<> hieFile
<> ", supported versions: 9141, 9124, 9122, 9121, 9103, 9102, 9101, 9084, 9083, 9082, 9081"
expected = userError message
nameCache <- newEmptyNameCache
readHieFile nameCache hieFile `shouldThrow` (== expected)
for_ supported \ (ghcVersion, hieVersion) -> do
let ghc = "ghc-" <> ghcVersion
context ("with " <> ghc) do
it "accepts HIE-files" do
withHieFile ghc \ hieFile -> do
extractSourceFileName hieFile `shouldReturn` "Foo.hs"
nameCache <- newEmptyNameCache
result <- readHieFile nameCache hieFile
result.hie_file_result_version `shouldBe` hieVersion
result.hie_file_result_ghc_version `shouldBe` fromString ghcVersion
let hie_file = result.hie_file_result
hie_file.hie_hs_file `shouldBe` "Foo.hs"
hie_file.hie_module `shouldBe` Module "main" "Foo"
length hie_file.hie_types `shouldBe` 1
Map.keys hie_file.hie_asts.getAsts `shouldBe` [HiePath "Foo.hs"]
length hie_file.hie_exports `shouldBe` 1
hie_file.hie_hs_src `shouldBe` B.unlines [
"module Foo where"
, "foo :: Int"
, "foo = 23"
]
length hie_file.hie_entity_infos `shouldBe` if hieVersion < 9121 then 0 else 3
it "maintains precise locations in NameCache" do
withSystemTempDirectory "hspec" \ dir -> do
createHieFile ghc dir "Foo.hs" [
"module Foo (foo) where"
, ""
, ""
, "foo :: Int"
, "foo = 23"
]
createHieFile ghc dir "Bar.hs" [
"module Bar where"
, "import qualified Foo"
, "bar :: Int"
, "bar = Foo.foo"
]
createHieFile ghc dir "Foo.hs" [
"module Foo (foo) where"
, "foo :: Int"
, "foo = 23"
]
nameCache <- newEmptyNameCache
_ <- readHieFile nameCache $ dir </> "Bar.hie"
_ <- readHieFile nameCache $ dir </> "Foo.hie"
names <- moduleEnvElts <$> readMVar (nsNames nameCache)
let
lookupFoo :: OccEnv Name -> Maybe Name
lookupFoo = flip lookupOccEnv $ mkOccName varName "foo"
candidates :: [Name]
candidates = mapMaybe lookupFoo names
expectedLocation :: SrcSpan
expectedLocation = mkSrcSpan (mkSrcLoc "Foo.hs" 3 1) (mkSrcLoc "Foo.hs" 3 4)
map nameSrcSpan candidates `shouldBe` [expectedLocation]