packages feed

hsinspect-0.1.0: library/HsInspect/Imports.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}

module HsInspect.Imports
  ( imports,
    Qualified,
  )
where

#if MIN_VERSION_GLASGOW_HASKELL(9,1,0,0)
import qualified GHC.Types.Target as GHC
#endif

#if MIN_VERSION_GLASGOW_HASKELL(9,3,0,0)
import qualified GHC.Driver.Env.Types as GHC
import qualified GHC.Unit.Env as GHC
import qualified GHC.Data.Bag as GHC
import qualified GHC.Types.Name.Reader as GHC
#elif MIN_VERSION_GLASGOW_HASKELL(9,0,0,0)
import qualified GHC.Types.Name.Reader as GHC
#else
import qualified HscTypes as GHC
import qualified RdrName as GHC
#endif

import Data.List (sort)
import Data.Maybe (fromJust)
import Data.Text (Text)
import qualified Data.Text as T
import qualified GHC as GHC
import HsInspect.Sexp
import HsInspect.Util
import HsInspect.Workarounds

imports :: GHC.GhcMonad m => FilePath -> m [Qualified]
imports file = do
  (fromJust -> m, target) <- importsOnly mempty file

  GHC.removeTarget $ GHC.TargetModule m
  GHC.addTarget target

  -- performance can be very bad here if the user hasn't compiled recently. We
  -- could do the Index hack and only load things that have .hi files but that
  -- will result in very bizarre behaviour and we don't expect the user's code
  -- to be compilable at this point.
#if MIN_VERSION_GLASGOW_HASKELL(9,3,0,0)
  sess <- GHC.getSession
  let unitid = GHC.ue_current_unit $ GHC.hsc_unit_env sess
  _ <- GHC.load $ GHC.LoadUpTo (GHC.mkModule unitid m)
#else
  _ <- GHC.load $ GHC.LoadUpTo m
#endif

  rdr_env <- minf_rdr_env' m
  pure . sort $ describe =<< GHC.globalRdrEnvElts rdr_env

describe :: GHC.GlobalRdrElt -> [Qualified]
describe GHC.GRE {GHC.gre_name, GHC.gre_imp} =
#if MIN_VERSION_GLASGOW_HASKELL(9,3,0,0)
  describe' <$> GHC.bagToList gre_imp
#else
  describe' <$> gre_imp
#endif
  where
    describe' GHC.ImpSpec {GHC.is_decl = GHC.ImpDeclSpec {GHC.is_mod, GHC.is_as, GHC.is_qual}} =
      let ln =
            if is_qual
              then Nothing
              else Just . T.pack $ showGhc gre_name
          lqn =
            if is_mod == is_as
              then Nothing
              else Just . T.pack $ showGhc is_as ++ "." ++ showGhc gre_name
          fqn = T.pack $ showGhc is_mod ++ "." ++ showGhc gre_name
       in Qualified ln lqn fqn

-- 1. local name
-- 2. locally qualified name
-- 3. fully qualified name
data Qualified
  = Qualified
      (Maybe Text)
      (Maybe Text)
      Text
  deriving (Eq, Ord, Show)
{- BOILERPLATE Qualified ToSexp field=[local,qual,full] -}
{- BOILERPLATE START -}
instance ToSexp Qualified where
  toSexp (Qualified p_1_1 p_1_2 p_1_3) = alist [("local", toSexp p_1_1), ("qual", toSexp p_1_2), ("full", toSexp p_1_3)]
{- BOILERPLATE END -}