packages feed

ghc-9.14.1: GHC/Driver/Session/Inspect.hs

{-# LANGUAGE LambdaCase #-}

-- | GHC API utilities for inspecting the GHC session
module GHC.Driver.Session.Inspect where

import GHC.Prelude
import GHC.Data.Maybe
import Control.Monad

import GHC.ByteCode.Types
import GHC.Core.FamInstEnv
import GHC.Core.InstEnv
import GHC.Driver.Env
import GHC.Driver.Main
import GHC.Driver.Monad
import GHC.Driver.Session
import GHC.Rename.Names
import GHC.Runtime.Context
import GHC.Runtime.Interpreter
import GHC.Types.Avail
import GHC.Types.Name
import GHC.Types.Name.Ppr
import GHC.Types.Name.Reader
import GHC.Types.Name.Set
import GHC.Types.PkgQual
import GHC.Types.SafeHaskell
import GHC.Types.SrcLoc
import GHC.Types.TyThing
import GHC.Types.TypeEnv
import GHC.Unit.External
import GHC.Unit.Home.ModInfo
import GHC.Unit.Module
import GHC.Unit.Module.Graph
import GHC.Unit.Module.ModDetails
import GHC.Unit.Module.ModIface
import GHC.Utils.Misc
import GHC.Utils.Outputable
import qualified GHC.Unit.Home.Graph as HUG

-- %************************************************************************
-- %*                                                                      *
--             Inspecting the session
-- %*                                                                      *
-- %************************************************************************

-- | Get the module dependency graph.
getModuleGraph :: GhcMonad m => m ModuleGraph -- ToDo: DiGraph ModSummary
getModuleGraph = liftM hsc_mod_graph getSession

{-# DEPRECATED isLoaded "Prefer 'isLoadedModule' and 'isLoadedHomeModule'" #-}
-- | Return @True@ \<==> module is loaded.
isLoaded :: GhcMonad m => ModuleName -> m Bool
isLoaded m = withSession $ \hsc_env -> liftIO $ do
  hmis <- HUG.lookupAllHug (hsc_HUG hsc_env) m
  return $! not (null hmis)

-- | Check whether a 'ModuleName' is found in the 'HomePackageTable'
-- for the given 'UnitId'.
isLoadedModule :: GhcMonad m => UnitId -> ModuleName -> m Bool
isLoadedModule uid m = withSession $ \hsc_env -> liftIO $ do
  hmi <- HUG.lookupHug (hsc_HUG hsc_env) uid m
  return $! isJust hmi

-- | Check whether 'Module' is part of the 'HomeUnitGraph'.
--
-- Similar to 'isLoadedModule', but for 'Module's.
isLoadedHomeModule :: GhcMonad m => Module -> m Bool
isLoadedHomeModule m = withSession $ \hsc_env -> liftIO $ do
  hmi <- HUG.lookupHugByModule m (hsc_HUG hsc_env)
  return $! isJust hmi

-- | Return the bindings for the current interactive session.
getBindings :: GhcMonad m => m [TyThing]
getBindings = withSession $ \hsc_env ->
    return $ icInScopeTTs $ hsc_IC hsc_env

-- | Return the instances for the current interactive session.
getInsts :: GhcMonad m => m ([ClsInst], [FamInst])
getInsts = withSession $ \hsc_env ->
    let (inst_env, fam_env) = ic_instances (hsc_IC hsc_env)
    in return (instEnvElts inst_env, fam_env)

getNamePprCtx :: GhcMonad m => m NamePprCtx
getNamePprCtx = withSession $ \hsc_env -> do
  return $ icNamePprCtx (hsc_unit_env hsc_env) (hsc_IC hsc_env)

-- | Container for information about a 'Module'.
data ModuleInfo = ModuleInfo {
        minf_type_env  :: TypeEnv,
        minf_exports   :: [AvailInfo],
        minf_instances :: [ClsInst],
        minf_iface     :: Maybe ModIface,
        minf_safe      :: SafeHaskellMode,
        minf_modBreaks :: Maybe InternalModBreaks
  }
        -- We don't want HomeModInfo here, because a ModuleInfo applies
        -- to package modules too.

-- | Request information about a loaded 'Module'
getModuleInfo :: GhcMonad m => Module -> m (Maybe ModuleInfo)  -- XXX: Maybe X
getModuleInfo mdl = withSession $ \hsc_env -> do
  if HUG.memberHugUnit (moduleUnit mdl) (hsc_HUG hsc_env)
        then liftIO $ getHomeModuleInfo hsc_env mdl
        else liftIO $ getPackageModuleInfo hsc_env mdl

getPackageModuleInfo :: HscEnv -> Module -> IO (Maybe ModuleInfo)
getPackageModuleInfo hsc_env mdl
  = do  eps <- hscEPS hsc_env
        iface <- hscGetModuleInterface hsc_env mdl
        let
            avails = mi_exports iface
            pte    = eps_PTE eps
            tys    = [ ty | name <- concatMap availNames avails,
                            Just ty <- [lookupTypeEnv pte name] ]

        return (Just (ModuleInfo {
                        minf_type_env  = mkTypeEnv tys,
                        minf_exports   = avails,
                        minf_instances = error "getModuleInfo: instances for package module unimplemented",
                        minf_iface     = Just iface,
                        minf_safe      = getSafeMode $ mi_trust iface,
                        minf_modBreaks = Nothing
                }))

availsToGlobalRdrEnv :: HasDebugCallStack => HscEnv -> Module -> [AvailInfo] -> IfGlobalRdrEnv
availsToGlobalRdrEnv hsc_env mod avails
  = forceGlobalRdrEnv rdr_env
    -- See Note [Forcing GREInfo] in GHC.Types.GREInfo.
  where
    rdr_env = mkGlobalRdrEnv (gresFromAvails hsc_env (Just imp_spec) avails)
      -- We're building a GlobalRdrEnv as if the user imported
      -- all the specified modules into the global interactive module
    imp_spec = ImpSpec { is_decl = decl, is_item = ImpAll}
    decl = ImpDeclSpec { is_mod = mod, is_as = moduleName mod,
                         is_qual = False, is_isboot = NotBoot, is_pkg_qual = NoPkgQual,
                         is_dloc = srcLocSpan interactiveSrcLoc,
                         is_level = NormalLevel }

getHomeModuleInfo :: HscEnv -> Module -> IO (Maybe ModuleInfo)
getHomeModuleInfo hsc_env mdl =
  HUG.lookupHugByModule mdl (hsc_HUG hsc_env) >>= \case
    Nothing  -> return Nothing
    Just hmi -> do
      let details  = hm_details hmi
          iface    = hm_iface hmi
      return (Just (ModuleInfo {
                        minf_type_env  = md_types details,
                        minf_exports   = md_exports details,
                         -- NB: already forced. See Note [Forcing GREInfo] in GHC.Types.GREInfo.
                        minf_instances = instEnvElts $ md_insts details,
                        minf_iface     = Just iface,
                        minf_safe      = getSafeMode $ mi_trust iface,
                        minf_modBreaks = getModBreaks hmi
                        }))

-- | The list of top-level entities defined in a module
modInfoTyThings :: ModuleInfo -> [TyThing]
modInfoTyThings minf = typeEnvElts (minf_type_env minf)

modInfoExports :: ModuleInfo -> [Name]
modInfoExports minf = concatMap availNames $! minf_exports minf

modInfoExportsWithSelectors :: ModuleInfo -> [Name]
modInfoExportsWithSelectors minf = concatMap availNames $! minf_exports minf

-- | Returns the instances defined by the specified module.
-- Warning: currently unimplemented for package modules.
modInfoInstances :: ModuleInfo -> [ClsInst]
modInfoInstances = minf_instances

modInfoIsExportedName :: ModuleInfo -> Name -> Bool
modInfoIsExportedName minf name = elemNameSet name (availsToNameSet (minf_exports minf))

mkNamePprCtxForModule ::
  GhcMonad m =>
  Module     ->
  ModuleInfo ->
  m NamePprCtx
mkNamePprCtxForModule mod minf = withSession $ \hsc_env -> do
  let name_ppr_ctx = mkNamePprCtx ptc (hsc_unit_env hsc_env) (availsToGlobalRdrEnv hsc_env mod (minf_exports minf))
      ptc = initPromotionTickContext (hsc_dflags hsc_env)
  return name_ppr_ctx

modInfoLookupName :: GhcMonad m =>
                     ModuleInfo -> Name
                  -> m (Maybe TyThing) -- XXX: returns a Maybe X
modInfoLookupName minf name = withSession $ \hsc_env -> do
   case lookupTypeEnv (minf_type_env minf) name of
     Just tyThing -> return (Just tyThing)
     Nothing      -> liftIO (lookupType hsc_env name)

modInfoIface :: ModuleInfo -> Maybe ModIface
modInfoIface = minf_iface

-- | Retrieve module safe haskell mode
modInfoSafe :: ModuleInfo -> SafeHaskellMode
modInfoSafe = minf_safe

modInfoModBreaks :: ModuleInfo -> Maybe InternalModBreaks
modInfoModBreaks = minf_modBreaks