serokell/importify

View on GitHub
src/Importify/Resolution/Explicit.hs

Summary

Maintainability
Test Coverage
-- | 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