src/Importify/Resolution/Explicit.hs
-- | Resolvers for removing symbols from explicit import lists.
module Importify.Resolution.Explicit
( collectUnusedSymbolsBy
, resolveModules
, symbolUsedIn
) where
import Universum
import Data.Data (Data)
import qualified Data.Map.Strict as M
import Language.Haskell.Exts (Module, ModuleName (..))
import Language.Haskell.Names (NameInfo (Export, GlobalSymbol, RecPatWildcard),
Scoped, Symbol (..), resolve,
symbolModule)
import Language.Haskell.Names.GlobalSymbolTable (Table)
import Importify.Syntax (anyAnnotation)
-- | Checks if 'Symbol' is used inside annotations. This function
-- needed to remove unused imports.
symbolUsedIn :: Symbol -> [Scoped l] -> Bool
symbolUsedIn symbol = anyAnnotation used
where
used :: NameInfo l -> Bool
-- Constructors are special because the whole type should be
-- considered used if one of its constructors is used
used (GlobalSymbol global@(Constructor smodule _sname stype) _) =
symbol == global ||
(symbolName symbol == stype && symbolModule symbol == smodule)
-- Symbol used as selectors; same as constuctors
used (GlobalSymbol global@(Selector smodule _sname stype _scons) _) =
symbol == global ||
(symbolName symbol == stype && symbolModule symbol == smodule)
-- The symbol is used itself
used (GlobalSymbol global _) = symbol == global
-- Symbol is used as a part of export declaration
used (Export symbols) = symbol `elem` symbols
-- Symbol used as wildcard record
used (RecPatWildcard symbols) = symbol `elem` symbols
-- Other symbols
used _ = False
-- | Collect symbols unused in annotations.
collectUnusedSymbolsBy
:: (Symbol -> Bool) -- ^ 'True' iff 'Symbol' is used
-> Table -- ^ Mapping from imported names to their symbols
-> [Symbol] -- ^ Returns list of unused symbols from 'Table'
collectUnusedSymbolsBy isUsed table = do
-- 1. For every pair (entity, its symbols) in Table
(_, importedSymbols) <- M.toList table
-- 2. And for every entity with same name
symbol <- importedSymbols
-- 3. Check whether this symbol used or not
guard $ not $ isUsed symbol
-- 4. If not found ⇒ unused
pure symbol
-- | Gather all symbols for given list of 'Module's.
resolveModules :: (Data l, Eq l) => [Module l] -> [(ModuleName (), [Symbol])]
resolveModules modules = M.toList $ resolve modules mempty