Syd Kerckhove
Syd Kerckhove

Reputation: 833

GHC API: Find all functions (and their types) in scope

I'm trying to list (print) all functions (and their types) that are in scope for a given module.

For example, I have this module:

{-# LANGUAGE NoImplicitPrelude #-}

module Reverse where

import Prelude ((==), String)

myString :: String
myString = "string"

I'm trying to use the GHC API (8.0.2), but I can't seem to find where the information that I'm looking for is stored. I have managed to find all functions in scope, (but not with their types,) with this code:

import Data.IORef

import DynFlags
import GHC
import GHC.LanguageExtensions
import GHC.Paths (libdir)
import HscTypes
import NameEnv
import OccName
import Outputable
import RdrName
import TcRnTypes


main = runGhc (Just libdir) $ do
        liftIO $ print sets
        dflags <- getSessionDynFlags
        let compdflags =
                (foldl xopt_set dflags [Cpp, ImplicitPrelude, MagicHash])
        setSessionDynFlags compdflags
        target <- guessTarget "Reverse.hs" Nothing
        setTargets [target]
        load LoadAllTargets
        modSum <- getModSummary $ mkModuleName "Reverse"
        parsedModule <- parseModule modSum
        tmod <- typecheckModule parsedModule
        let (tcenv, moddets) = tm_internals_ tmod
        printO $ map (map gre_name) $ occEnvElts $ tcg_rdr_env tcenv

printO
    :: (GhcMonad m, Outputable a)
    => a -> m ()
printO a = do
    dfs <- getProgramDynFlags
    liftIO $ putStrLn $ showPpr dfs a

I get this output:

[[String], [==], [myString]]

Of course, this is only half of the way to the data I need.

Upvotes: 2

Views: 304

Answers (1)

Syd Kerckhove
Syd Kerckhove

Reputation: 833

The GHC API is rather confusing, you have to get used to a lot of abbreviations, type-synonyms and a style-heterogenous codebase, but finding the the name and type of everything in scope should be possible. Otherwise GHC couldn't tell you that your function is not in scope if you make a typo.

Indeed, once you have type-checked a module, all the relevant information is available. First you need all the Names of your functions, which you can get with this code:

parsedModule <- parseModule modSum
tmod <- typecheckModule parsedModule
let (tcenv, moddets) = tm_internals_ tmod
let names = concatMap (map gre_name) $ occEnvElts $ tcg_rdr_env tcenv

Then you need to lookup the Names to get TyThings with lookupName. You'll get a Maybe TyThing (Nothing if the Name is not found), and when the name refers to a function, the TyThing will be AnId i where i is the thing you're looking for. An Id is just a name with a type. You can then get the type with varType.

You could argue that all these types make this problem a lot harder, but they've made it possible for me to figure out what I need to do without looking into the code and with no documentation.

Upvotes: 2

Related Questions