HaRe-0.8.3.0: src/Language/Haskell/Refact/Utils/GhcModuleGraph.hs
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE CPP #-}
module Language.Haskell.Refact.Utils.GhcModuleGraph
(
getModulesAsGraph
, summaryNodeSummary
) where
-- GHC imports
#if __GLASGOW_HASKELL__ > 710
import BasicTypes
#endif
import Digraph
import FastString
import GHC
import HscTypes
import Panic
-- Other imports
import qualified Data.Map as Map
{-
--------------------------------------------------
graph:
Vertices:
(ModSummary {main:Main,},1,[3])
(ModSummary {main:S1,}, 2, [])
(ModSummary {main:M3,}, 3,[4])
(ModSummary {main:M2,}, 4,[2])
Edges:
(ModSummary {main:Main,},1,[3]) -> (ModSummary {main:M3,},3,[4])
(ModSummary {main:M3,}, 3,[4]) -> (ModSummary {main:M2,},4,[2])
(ModSummary {main:M2,}, 4,[2]) -> (ModSummary {main:S1,},2,[])
transposeG:
Vertices:
(ModSummary {main:Main,}, 1,[3])
(ModSummary {main:S1,}, 2, [])
(ModSummary {main:M3,}, 3,[4])
(ModSummary {main:M2,}, 4,[2])
Edges:
(ModSummary {main:S1,}, 2, []) -> (ModSummary {main:M2,}, 4,[2])
(ModSummary {main:M3,}, 3,[4]) -> (ModSummary {main:Main,}, 1,[3])
(ModSummary {main:M2,}, 4,[2]) -> (ModSummary {main:M3,}, 3,[4])
-}
-- ---------------------------------------------------------------------
getModulesAsGraph
:: Bool -> [ModSummary] -> Maybe ModuleName -> Graph SummaryNode
getModulesAsGraph drop_hs_boot_nodes summaries mb_root_mod
= initial_graph
-- = map (fmap summaryNodeSummary) $ stronglyConnCompG initial_graph
where
(graph, lookup_node) = moduleGraphNodes drop_hs_boot_nodes summaries
initial_graph = case mb_root_mod of
Nothing -> graph
Just root_mod ->
-- restrict the graph to just those modules reachable from
-- the specified module. We do this by building a graph with
-- the full set of nodes, and determining the reachable set from
-- the specified node.
let root | Just node <- lookup_node HsSrcFile root_mod, graph `hasVertexG` node = node
| otherwise = panic "module does not exist"
in graphFromEdgedVertices (seq root (reachableG graph root))
-- ---------------------------------------------------------------------
-- This bit is from the GHC source >>>>>>>
type SummaryNode = (ModSummary, Int, [Int])
{-
topSortModuleGraph
:: Bool -> [ModSummary] -> Maybe ModuleName -> [SCC ModSummary]
topSortModuleGraph drop_hs_boot_nodes summaries mb_root_mod
= map (fmap summaryNodeSummary) $ stronglyConnCompG initial_graph
where
(graph, lookup_node) = moduleGraphNodes drop_hs_boot_nodes summaries
initial_graph = case mb_root_mod of
Nothing -> graph
Just root_mod ->
-- restrict the graph to just those modules reachable from
-- the specified module. We do this by building a graph with
-- the full set of nodes, and determining the reachable set from
-- the specified node.
let root | Just node <- lookup_node HsSrcFile root_mod, graph `hasVertexG` node = node
| otherwise = ghcError (ProgramError "module does not exist")
in graphFromEdgedVertices (seq root (reachableG graph root))
-}
summaryNodeKey :: SummaryNode -> Int
summaryNodeKey (_, k, _) = k
summaryNodeSummary :: SummaryNode -> ModSummary
summaryNodeSummary (s, _, _) = s
moduleGraphNodes :: Bool -> [ModSummary]
-> (Graph SummaryNode, HscSource -> ModuleName -> Maybe SummaryNode)
moduleGraphNodes drop_hs_boot_nodes summaries = (graphFromEdgedVertices nodes, lookup_node)
where
numbered_summaries = zip summaries [1..]
lookup_node :: HscSource -> ModuleName -> Maybe SummaryNode
lookup_node hs_src modName = Map.lookup (modName, hs_src) node_map
lookup_key :: HscSource -> ModuleName -> Maybe Int
lookup_key hs_src modName = fmap summaryNodeKey (lookup_node hs_src modName)
node_map :: NodeMap SummaryNode
node_map = Map.fromList [ ((moduleName (ms_mod s), ms_hsc_src s), node)
| node@(s, _, _) <- nodes ]
-- We use integers as the keys for the SCC algorithm
nodes :: [SummaryNode]
nodes = [ (s, key, out_keys)
| (s, key) <- numbered_summaries
-- Drop the hi-boot ones if told to do so
, not (isBootSummary s && drop_hs_boot_nodes)
, let out_keys = out_edge_keys hs_boot_key (map unLoc (ms_home_srcimps s)) ++
out_edge_keys HsSrcFile (map unLoc (ms_home_imps s)) ++
(-- see [boot-edges] below
if drop_hs_boot_nodes || ms_hsc_src s == HsBootFile
then []
else case lookup_key HsBootFile (ms_mod_name s) of
Nothing -> []
Just k -> [k]) ]
-- [boot-edges] if this is a .hs and there is an equivalent
-- .hs-boot, add a link from the former to the latter. This
-- has the effect of detecting bogus cases where the .hs-boot
-- depends on the .hs, by introducing a cycle. Additionally,
-- it ensures that we will always process the .hs-boot before
-- the .hs, and so the HomePackageTable will always have the
-- most up to date information.
-- Drop hs-boot nodes by using HsSrcFile as the key
hs_boot_key | drop_hs_boot_nodes = HsSrcFile
| otherwise = HsBootFile
out_edge_keys :: HscSource -> [ModuleName] -> [Int]
out_edge_keys hi_boot ms = mapCatMaybes (lookup_key hi_boot) ms
-- If we want keep_hi_boot_nodes, then we do lookup_key with
-- the IsBootInterface parameter True; else False
mapCatMaybes :: (a -> Maybe b) -> [a] -> [b]
mapCatMaybes _ [] = []
mapCatMaybes f (x:xs) = case f x of
Just y -> y : mapCatMaybes f xs
Nothing -> mapCatMaybes f xs
type NodeKey = (ModuleName, HscSource) -- The nodes of the graph are
type NodeMap a = Map.Map NodeKey a -- keyed by (mod, src_file_type) pairs
{-
msKey :: ModSummary -> NodeKey
msKey (ModSummary { ms_mod = mod, ms_hsc_src = boot }) = (moduleName mod,boot)
mkNodeMap :: [ModSummary] -> NodeMap ModSummary
mkNodeMap summaries = Map.fromList [ (msKey s, s) | s <- summaries]
nodeMapElts :: NodeMap a -> [a]
nodeMapElts = Map.elems
-}
{-
msDeps :: ModSummary -> [(Located ModuleName, IsBootInterface)]
-- (msDeps s) returns the dependencies of the ModSummary s.
-- A wrinkle is that for a {-# SOURCE #-} import we return
-- *both* the hs-boot file
-- *and* the source file
-- as "dependencies". That ensures that the list of all relevant
-- modules always contains B.hs if it contains B.hs-boot.
-- Remember, this pass isn't doing the topological sort. It's
-- just gathering the list of all relevant ModSummaries
msDeps s =
concat [ [(m,True), (m,False)] | m <- ms_home_srcimps s ]
++ [ (m,False) | m <- ms_home_imps s ]
-}
#if __GLASGOW_HASKELL__ <= 710
home_imps :: [Located (ImportDecl RdrName)] -> [Located ModuleName]
home_imps imps = [ ideclName i | L _ i <- imps, isLocal (ideclPkgQual i) ]
where isLocal Nothing = True
isLocal (Just pkg) | pkg == fsLit "this" = True -- "this" is special
isLocal _ = False
#endif
{-
ms_home_allimps :: ModSummary -> [ModuleName]
ms_home_allimps ms = map unLoc (ms_home_srcimps ms ++ ms_home_imps ms)
-}
ms_home_srcimps :: ModSummary -> [Located ModuleName]
#if __GLASGOW_HASKELL__ <= 710
ms_home_srcimps = home_imps . ms_srcimps
#else
ms_home_srcimps ms = map snd $ ms_srcimps ms
#endif
ms_home_imps :: ModSummary -> [Located ModuleName]
#if __GLASGOW_HASKELL__ <= 710
ms_home_imps = home_imps . ms_imps
#else
ms_home_imps ms = map snd $ ms_imps ms
#endif
-- GHC source end