diff options
Diffstat (limited to 'src/Main.hs')
-rw-r--r-- | src/Main.hs | 236 |
1 files changed, 130 insertions, 106 deletions
diff --git a/src/Main.hs b/src/Main.hs index 7b68fc4e..550884f5 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -17,7 +17,7 @@ import Paths_haddock ( getDataDir ) import Prelude hiding ( catch ) import Control.Exception ( catch ) -import Control.Monad ( when, liftM ) +import Control.Monad ( when, liftM, foldM ) import Control.Monad.Writer ( Writer, runWriter, tell ) import Data.Char ( isSpace ) import Data.IORef ( writeIORef ) @@ -58,7 +58,7 @@ import StaticFlags ( parseStaticFlags ) ----------------------------------------------------------------------------- -- Top-level stuff -type CheckedMod = (Module, FullyCheckedMod, FilePath) +type CheckedMod = (Module, FilePath, FullyCheckedMod) main :: IO () main = do @@ -208,16 +208,17 @@ sortAndCheckModules session flags files = defaultErrorHandler flags $ do checkedMod <- case mbMod of Just m -> return m Nothing -> die ("Failed to load module: " ++ moduleString mod) - return (mod, checkedMod, file)) modsAndFiles + return (mod, file, checkedMod)) modsAndFiles ensureFullyChecked checkedMods where ensureFullyChecked modules | length modules' == length modules = return modules' | otherwise = die "Failed to check all modules properly\n" - where modules' = [ (mod, (a,b,c,d), f) | - (mod, CheckedModule a (Just b) (Just c) (Just d), f) + where modules' = [ (mod, f, (a,b,c,d)) | + (mod, f, CheckedModule a (Just b) (Just c) (Just d)) <- modules ] + data Flag = Flag_CSS String | Flag_Debug @@ -389,8 +390,12 @@ run flags modules extEnv = do prologue <- getPrologue flags - let - (modMap, messages) = runWriter (pass1 modules flags) + let + -- collect the data from GHC that we need for each home module + ghcModuleData = map moduleDataGHC modules + -- run pass 1 on this data + (modMap, messages) = runWriter (pass1 ghcModuleData flags) + haddockMods = catMaybes [ Map.lookup mod modMap | (mod,_,_) <- modules ] homeEnv = buildGlobalDocEnv haddockMods env = homeEnv `Map.union` extEnv @@ -453,95 +458,115 @@ type FullyCheckedMod = (ParsedSource, printEntity (DocEntity doc) = show doc printEntity (DeclEntity name) = show $ ppr name defaultUserStyle -pass1 :: [CheckedMod] -> [Flag] -> ErrMsgM ModuleMap -pass1 modules flags = worker modules (Map.empty) flags - where - worker [] moduleMap _ = return moduleMap - worker ((mod, checked_mod, filename):rest_modules) moduleMap flags = do - - let (parsed_source, renamed_source, _, moduleInfo) = checked_mod - (mb_doc_opts, _, _) = getModuleStuff parsed_source - - opts <- mkDocOpts mb_doc_opts - - let (group, _, mb_exports, mbModDoc, haddockModInfo) = renamed_source +-- | This data structure collects all the information we need from the GHC API +-- about a home package module +data ModuleDataGHC = ModuleDataGHC { + ghcModule :: Module, + ghcFilename :: FilePath, + ghcMbDocOpts :: Maybe String, + ghcHaddockModInfo :: HaddockModInfo Name, + ghcMbDoc :: Maybe (HsDoc Name), + ghcGroup :: HsGroup Name, + ghcMbExports :: Maybe [LIE Name], + ghcExportedNames :: [Name], + ghcNamesInScope :: [Name], + ghcInstances :: [Instance] +} - entities = (reverse . nubBy sameName . hs_docs) group - exports = fmap (map unLoc) mb_exports +-- | Dig out what we want from the GHC API without altering anything +moduleDataGHC :: CheckedMod -> ModuleDataGHC +moduleDataGHC (mod, file, checkedMod) = ModuleDataGHC { + ghcModule = mod, + ghcFilename = file, + ghcMbDocOpts = mbOpts, + ghcHaddockModInfo = info, + ghcMbDoc = mbDoc, + ghcGroup = group, + ghcMbExports = mbExports, + ghcExportedNames = modInfoExports modInfo, + ghcNamesInScope = fromJust $ modInfoTopLevelScope modInfo, + ghcInstances = modInfoInstances modInfo +} + where + HsModule _ _ _ _ _ mbOpts _ _ = unLoc parsed + (group, _, mbExports, mbDoc, info) = renamed + (parsed, renamed, _, modInfo) = checkedMod + +-- | Massage the data in ModuleDataGHC to produce something closer to what +-- we want to render. To do this, we need access to modules before this one +-- in the topological sort, to which we have already done this conversion. +-- That's what's in the ModuleMap. +pass1data :: ModuleDataGHC -> [Flag] -> ModuleMap -> ErrMsgM HaddockModule +pass1data modData flags modMap = do + + let mod = ghcModule modData + + opts <- mkDocOpts (ghcMbDocOpts modData) mod + + let group = ghcGroup modData + entities = (reverse . nubBy sameName . hs_docs) group + exports = fmap (map unLoc) (ghcMbExports modData) + entityNames_ = entityNames entities + subNames = allSubNames group + localNames = entityNames_ ++ subNames + subMap = mkSubMap group + expDeclMap = mkDeclMap (ghcExportedNames modData) group + localDeclMap = mkDeclMap entityNames_ group + docMap = mkDocMap group + ignoreExps = Flag_IgnoreAllExports `elem` flags + + visibleNames <- mkVisibleNames mod modMap localNames (ghcNamesInScope modData) + subMap exports opts localDeclMap + + exportItems <- mkExportItems modMap mod (ghcExportedNames modData) + expDeclMap localDeclMap subMap entities + opts exports ignoreExps docMap + + -- prune the export list to just those declarations that have + -- documentation, if the 'prune' option is on. + let + prunedExportItems + | OptPrune `elem` opts = pruneExportItems exportItems + | otherwise = exportItems - -- lots of names - exportedNames = modInfoExports moduleInfo - theseEntityNames = entityNames entities - subNames = allSubnamesInGroup group - localNames = theseEntityNames ++ subNames - -- guaranteed to be Just, since the module has been compiled from - -- scratch - scopeNames = fromJust $ modInfoTopLevelScope moduleInfo - - subMap = mk_sub_map_from_group group - - exportedDeclMap = mkDeclMap exportedNames group - localDeclMap = mkDeclMap theseEntityNames group - docMap = mkDocMap group + return HM { + hmod_mod = mod, + hmod_orig_filename = ghcFilename modData, + hmod_info = ghcHaddockModInfo modData, + hmod_doc = ghcMbDoc modData, + hmod_rn_doc = Nothing, + hmod_options = opts, + hmod_locals = localNames, + hmod_doc_map = docMap, + hmod_rn_doc_map = Map.empty, + hmod_sub_map = subMap, + hmod_export_items = prunedExportItems, + hmod_rn_export_items = [], + hmod_exports = ghcExportedNames modData, + hmod_visible_exports = visibleNames, + hmod_exported_decl_map = expDeclMap, + hmod_instances = ghcInstances modData + } + where + mkDocOpts mbOpts mod = do + opts <- case mbOpts of + Just opts -> processOptions opts + Nothing -> return [] + let opts' = if Flag_HideModule (moduleString mod) `elem` flags + then OptHide : opts + else opts + return opts' + +-- | Produce a map of HaddockModules with information that is close to +-- renderable. What is lacking after this pass are the renamed export items. +pass1 :: [ModuleDataGHC] -> [Flag] -> ErrMsgM ModuleMap +pass1 modules flags = foldM produceAndInsert Map.empty modules + where + produceAndInsert modMap modData = do + resultMod <- pass1data modData flags modMap + let key = ghcModule modData + return (Map.insert key resultMod modMap) - ignoreAllExports = Flag_IgnoreAllExports `elem` flags - - packageId = modulePackageId mod - - theseVisibleNames <- visibleNames mod moduleMap localNames scopeNames - subMap exports opts localDeclMap - packageId - - exportItems <- mkExportItems moduleMap mod exportedNames - exportedDeclMap localDeclMap subMap entities - opts exports ignoreAllExports docMap - packageId - - -- prune the export list to just those declarations that have - -- documentation, if the 'prune' option is on. - let prunedExportItems - | OptPrune `elem` opts = pruneExportItems exportItems - | otherwise = exportItems - - instances = modInfoInstances moduleInfo - - haddock_module = HM { - hmod_mod = mod, - hmod_orig_filename = filename, - hmod_info = haddockModInfo, - hmod_doc = mbModDoc, - hmod_rn_doc = Nothing, - hmod_options = opts, - hmod_locals = localNames, - hmod_doc_map = docMap, - hmod_rn_doc_map = Map.empty, - hmod_sub_map = subMap, - hmod_export_items = prunedExportItems, - hmod_rn_export_items = [], - hmod_exports = exportedNames, - hmod_visible_exports = theseVisibleNames, - hmod_exported_decl_map = exportedDeclMap, - hmod_instances = instances - } - - moduleMap' = Map.insert mod haddock_module moduleMap - - worker rest_modules moduleMap' flags - - where - getModuleStuff source = - let HsModule _ _ _ _ _ mb_opts info mb_doc = unLoc source - in (mb_opts, info, mb_doc) - - mkDocOpts mbOpts = do - opts <- case mbOpts of - Just opts -> processOptions opts - Nothing -> return [] - let opts' = if Flag_HideModule (moduleString mod) `elem` flags - then OptHide : opts - else opts - return opts' - sameName (DocEntity _) _ = False sameName (DeclEntity _) (DocEntity _) = False sameName (DeclEntity a) (DeclEntity b) = a == b @@ -593,14 +618,13 @@ finishedDoc d DocEmpty rest = rest finishedDoc (DeclEntity name) doc rest = (name, doc) : rest finishedDoc _ _ rest = rest -allSubnamesInGroup :: HsGroup Name -> [Name] -allSubnamesInGroup group = +allSubNames :: HsGroup Name -> [Name] +allSubNames group = concat [ tail (map unLoc (tyClDeclNames tycld)) | L _ tycld <- hs_tyclds group ] -mk_sub_map_from_group :: HsGroup Name -> Map Name [Name] -mk_sub_map_from_group group = - Map.fromList [ (name, subs) | L _ tycld <- hs_tyclds group, - let name:subs = map unLoc (tyClDeclNames tycld) ] +mkSubMap :: HsGroup Name -> Map Name [Name] +mkSubMap group = Map.fromList [ (name, subs) | L _ tycld <- hs_tyclds group, + let name:subs = map unLoc (tyClDeclNames tycld) ] mkDeclMap :: [Name] -> HsGroup Name -> Map Name (LHsDecl Name) mkDeclMap names group = Map.fromList [ (n,d) | (n,Just d) <- maybeDecls ] @@ -757,11 +781,10 @@ mkExportItems -> Maybe [IE Name] -> Bool -- --ignore-all-exports flag -> Map Name (HsDoc Name) - -> PackageId -> ErrMsgM [ExportItem Name] mkExportItems mod_map this_mod exported_names exportedDeclMap localDeclMap sub_map entities - opts maybe_exps ignore_all_exports docMap packageId + opts maybe_exps ignore_all_exports docMap | isNothing maybe_exps || ignore_all_exports || OptIgnoreExports `elem` opts = everything_local_exported | Just specs <- maybe_exps = do @@ -770,6 +793,8 @@ mkExportItems mod_map this_mod exported_names exportedDeclMap localDeclMap sub_m where everything_local_exported = -- everything exported return (fullContentsOfThisModule this_mod entities localDeclMap docMap) + + packageId = modulePackageId this_mod lookupExport (IEVar x) = declWith x lookupExport (IEThingAbs t) = declWith t @@ -899,7 +924,7 @@ pruneExportItems items = filter hasDoc items -- ----------------------------------------------------------------------------- -- Gather a list of original names exported from this module -visibleNames :: Module +mkVisibleNames :: Module -> ModuleMap -> [Name] -> [Name] @@ -907,10 +932,9 @@ visibleNames :: Module -> Maybe [IE Name] -> [DocOption] -> Map Name (LHsDecl Name) - -> PackageId -> ErrMsgM [Name] -visibleNames mdl modMap localNames scope subMap maybeExps opts declMap packageId +mkVisibleNames mdl modMap localNames scope subMap maybeExps opts declMap -- if no export list, just return all local names | Nothing <- maybeExps = return (filter hasDecl localNames) | OptIgnoreExports `elem` opts = return localNames @@ -934,8 +958,8 @@ visibleNames mdl modMap localNames scope subMap maybeExps opts declMap packageId IEThingWith t cs -> return (t : cs) IEModuleContents m - | mkModule packageId m == mdl -> return localNames - | otherwise -> let m' = mkModule packageId m in + | mkModule (modulePackageId mdl) m == mdl -> return localNames + | otherwise -> let m' = mkModule (modulePackageId mdl) m in case Map.lookup m' modMap of Just mod | OptHide `elem` hmod_options mod -> |