Commit 85309a3c authored by Simon Jakobi's avatar Simon Jakobi Committed by Ben Gamari

Serialize docstrings to ifaces, display them with new GHCi :doc command

If `-haddock` is set, we now extract docstrings from the renamed ast
and serialize them in the .hi-files.

This includes some of the changes from D4749 with the notable
exceptions of the docstring lexing and renaming.

A currently limited and experimental GHCi :doc command can be used
to display docstrings for declarations.

The formatting of pretty-printed docstrings is changed slightly,
causing some changes in testsuite/tests/haddock.

Test Plan: ./validate

Reviewers: alexbiehl, hvr, gershomb, harpocrates, bgamari

Reviewed By: alexbiehl

Subscribers: rwbarton, thomie, carter

Differential Revision: https://phabricator.haskell.org/D4758
parent aa77c602
......@@ -60,6 +60,7 @@ import Coverage
import Util
import MonadUtils
import OrdList
import ExtractDocs
import Data.List
import Data.IORef
......@@ -183,6 +184,8 @@ deSugar hsc_env
; foreign_files <- readIORef th_foreign_files_var
; let (doc_hdr, decl_docs, arg_docs) = extractDocs tcg_env
; let mod_guts = ModGuts {
mg_module = mod,
mg_hsc_src = hsc_src,
......@@ -209,7 +212,10 @@ deSugar hsc_env
mg_modBreaks = modBreaks,
mg_safe_haskell = safe_mode,
mg_trust_pkg = imp_trust_own_pkg imports,
mg_complete_sigs = complete_matches
mg_complete_sigs = complete_matches,
mg_doc_hdr = doc_hdr,
mg_decl_docs = decl_docs,
mg_arg_docs = arg_docs
}
; return (msgs, Just mod_guts)
}}}}
......
This diff is collapsed.
......@@ -310,6 +310,7 @@ Library
DsMonad
DsUsage
DsUtils
ExtractDocs
Match
MatchCon
MatchLit
......
{-# LANGUAGE CPP, DeriveDataTypeable #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module HsDoc
( HsDocString
......@@ -8,33 +10,59 @@ module HsDoc
, unpackHDS
, hsDocStringToByteString
, ppr_mbDoc
, appendDocs
, concatDocs
, DeclDocMap(..)
, emptyDeclDocMap
, ArgDocMap(..)
, emptyArgDocMap
) where
#include "HsVersions.h"
import GhcPrelude
import Binary
import Encoding
import FastFunctions
import Name
import Outputable
import SrcLoc
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C8
import qualified Data.ByteString.Internal as BS
import Data.Data
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import Foreign
-- | Haskell Documentation String
--
-- Internally this is a UTF8-Encoded 'ByteString'.
newtype HsDocString = HsDocString ByteString
-- There are at least two plausible Semigroup instances for this type:
--
-- 1. Simple string concatenation.
-- 2. Concatenation as documentation paragraphs with newlines in between.
--
-- To avoid confusion, we pass on defining an instance at all.
deriving (Eq, Show, Data)
-- | Located Haskell Documentation String
type LHsDocString = Located HsDocString
instance Binary HsDocString where
put_ bh (HsDocString bs) = put_ bh bs
get bh = HsDocString <$> get bh
instance Outputable HsDocString where
ppr = text . unpackHDS
ppr = doubleQuotes . text . unpackHDS
mkHsDocString :: String -> HsDocString
mkHsDocString s =
......@@ -59,3 +87,63 @@ hsDocStringToByteString (HsDocString bs) = bs
ppr_mbDoc :: Maybe LHsDocString -> SDoc
ppr_mbDoc (Just doc) = ppr doc
ppr_mbDoc Nothing = empty
-- | Join two docstrings.
--
-- Non-empty docstrings are joined with two newlines in between,
-- resulting in separate paragraphs.
appendDocs :: HsDocString -> HsDocString -> HsDocString
appendDocs x y =
fromMaybe
(HsDocString BS.empty)
(concatDocs [x, y])
-- | Concat docstrings with two newlines in between.
--
-- Empty docstrings are skipped.
--
-- If all inputs are empty, 'Nothing' is returned.
concatDocs :: [HsDocString] -> Maybe HsDocString
concatDocs xs =
if BS.null b
then Nothing
else Just (HsDocString b)
where
b = BS.intercalate (C8.pack "\n\n")
. filter (not . BS.null)
. map hsDocStringToByteString
$ xs
-- | Docs for declarations: functions, data types, instances, methods etc.
newtype DeclDocMap = DeclDocMap (Map Name HsDocString)
instance Binary DeclDocMap where
put_ bh (DeclDocMap m) = put_ bh (Map.toAscList m)
get bh = DeclDocMap . Map.fromDistinctAscList <$> get bh
instance Outputable DeclDocMap where
ppr (DeclDocMap m) = vcat (map pprPair (Map.toAscList m))
where
pprPair (name, doc) = ppr name Outputable.<> colon $$ nest 2 (ppr doc)
emptyDeclDocMap :: DeclDocMap
emptyDeclDocMap = DeclDocMap Map.empty
-- | Docs for arguments. E.g. function arguments, method arguments.
newtype ArgDocMap = ArgDocMap (Map Name (Map Int HsDocString))
instance Binary ArgDocMap where
put_ bh (ArgDocMap m) = put_ bh (Map.toAscList (Map.toAscList <$> m))
get bh = ArgDocMap . fmap Map.fromDistinctAscList . Map.fromDistinctAscList
<$> get bh
instance Outputable ArgDocMap where
ppr (ArgDocMap m) = vcat (map pprPair (Map.toAscList m))
where
pprPair (name, int_map) =
ppr name Outputable.<> colon $$ nest 2 (pprIntMap int_map)
pprIntMap im = vcat (map pprIPair (Map.toAscList im))
pprIPair (i, doc) = ppr i Outputable.<> colon $$ nest 2 (ppr doc)
emptyArgDocMap :: ArgDocMap
emptyArgDocMap = ArgDocMap Map.empty
......@@ -1090,6 +1090,9 @@ pprModIface iface
, pprTrustInfo (mi_trust iface)
, pprTrustPkg (mi_trust_pkg iface)
, vcat (map ppr (mi_complete_sigs iface))
, text "module header:" $$ nest 2 (ppr (mi_doc_hdr iface))
, text "declaration docs:" $$ nest 2 (ppr (mi_decl_docs iface))
, text "arg docs:" $$ nest 2 (ppr (mi_arg_docs iface))
]
where
pp_hsc_src HsBootFile = text "[boot]"
......
......@@ -108,6 +108,7 @@ import Fingerprint
import Exception
import UniqSet
import Packages
import ExtractDocs
import Control.Monad
import Data.Function
......@@ -152,12 +153,17 @@ mkIface hsc_env maybe_old_fingerprint mod_details
mg_warns = warns,
mg_hpc_info = hpc_info,
mg_safe_haskell = safe_mode,
mg_trust_pkg = self_trust
mg_trust_pkg = self_trust,
mg_doc_hdr = doc_hdr,
mg_decl_docs = decl_docs,
mg_arg_docs = arg_docs
}
= mkIface_ hsc_env maybe_old_fingerprint
this_mod hsc_src used_th deps rdr_env fix_env
warns hpc_info self_trust
safe_mode usages mod_details
safe_mode usages
doc_hdr decl_docs arg_docs
mod_details
-- | make an interface from the results of typechecking only. Useful
-- for non-optimising compilation, or where we aren't generating any
......@@ -198,11 +204,16 @@ mkIfaceTc hsc_env maybe_old_fingerprint safe_mode mod_details
-- module and does not need to be recorded as a dependency.
-- See Note [Identity versus semantic module]
usages <- mkUsageInfo hsc_env this_mod (imp_mods imports) used_names dep_files merged
let (doc_hdr', doc_map, arg_map) = extractDocs tc_result
mkIface_ hsc_env maybe_old_fingerprint
this_mod hsc_src
used_th deps rdr_env
fix_env warns hpc_info
(imp_trust_own_pkg imports) safe_mode usages mod_details
(imp_trust_own_pkg imports) safe_mode usages
doc_hdr' doc_map arg_map
mod_details
......@@ -212,11 +223,15 @@ mkIface_ :: HscEnv -> Maybe Fingerprint -> Module -> HscSource
-> Bool
-> SafeHaskellMode
-> [Usage]
-> Maybe HsDocString
-> DeclDocMap
-> ArgDocMap
-> ModDetails
-> IO (ModIface, Bool)
mkIface_ hsc_env maybe_old_fingerprint
this_mod hsc_src used_th deps rdr_env fix_env src_warns
hpc_info pkg_trust_req safe_mode usages
doc_hdr decl_docs arg_docs
ModDetails{ md_insts = insts,
md_fam_insts = fam_insts,
md_rules = rules,
......@@ -304,7 +319,10 @@ mkIface_ hsc_env maybe_old_fingerprint
-- And build the cached values
mi_warn_fn = mkIfaceWarnCache warns,
mi_fix_fn = mkIfaceFixCache fixities,
mi_complete_sigs = icomplete_sigs }
mi_complete_sigs = icomplete_sigs,
mi_doc_hdr = doc_hdr,
mi_decl_docs = decl_docs,
mi_arg_docs = arg_docs }
(new_iface, no_change_at_all)
<- {-# SCC "versioninfo" #-}
......
......@@ -132,6 +132,9 @@ module GHC (
ForeignHValue,
compileExprRemote, compileParsedExprRemote,
-- ** Docs
getDocs, GetDocsFailure(..),
-- ** Other
runTcInteractive, -- Desired by some clients (Trac #8878)
isStmt, hasImport, isImport, isDecl,
......
......@@ -950,7 +950,16 @@ data ModIface
-- itself) but imports some trustworthy modules from its own
-- package (which does require its own package be trusted).
-- See Note [RnNames . Trust Own Package]
mi_complete_sigs :: [IfaceCompleteMatch]
mi_complete_sigs :: [IfaceCompleteMatch],
mi_doc_hdr :: Maybe HsDocString,
-- ^ Module header.
mi_decl_docs :: DeclDocMap,
-- ^ Docs on declarations.
mi_arg_docs :: ArgDocMap
-- ^ Docs on arguments.
}
-- | Old-style accessor for whether or not the ModIface came from an hs-boot
......@@ -1028,7 +1037,10 @@ instance Binary ModIface where
mi_hpc = hpc_info,
mi_trust = trust,
mi_trust_pkg = trust_pkg,
mi_complete_sigs = complete_sigs }) = do
mi_complete_sigs = complete_sigs,
mi_doc_hdr = doc_hdr,
mi_decl_docs = decl_docs,
mi_arg_docs = arg_docs }) = do
put_ bh mod
put_ bh sig_of
put_ bh hsc_src
......@@ -1057,6 +1069,9 @@ instance Binary ModIface where
put_ bh trust
put_ bh trust_pkg
put_ bh complete_sigs
lazyPut bh doc_hdr
lazyPut bh decl_docs
lazyPut bh arg_docs
get bh = do
mod <- get bh
......@@ -1087,6 +1102,9 @@ instance Binary ModIface where
trust <- get bh
trust_pkg <- get bh
complete_sigs <- get bh
doc_hdr <- lazyGet bh
decl_docs <- lazyGet bh
arg_docs <- lazyGet bh
return (ModIface {
mi_module = mod,
mi_sig_of = sig_of,
......@@ -1120,7 +1138,10 @@ instance Binary ModIface where
mi_warn_fn = mkIfaceWarnCache warns,
mi_fix_fn = mkIfaceFixCache fixities,
mi_hash_fn = mkIfaceHashCache decls,
mi_complete_sigs = complete_sigs })
mi_complete_sigs = complete_sigs,
mi_doc_hdr = doc_hdr,
mi_decl_docs = decl_docs,
mi_arg_docs = arg_docs })
-- | The original names declared of a certain module that are exported
type IfaceExport = AvailInfo
......@@ -1159,7 +1180,10 @@ emptyModIface mod
mi_hpc = False,
mi_trust = noIfaceTrustInfo,
mi_trust_pkg = False,
mi_complete_sigs = [] }
mi_complete_sigs = [],
mi_doc_hdr = Nothing,
mi_decl_docs = emptyDeclDocMap,
mi_arg_docs = emptyArgDocMap }
-- | Constructs cache for the 'mi_hash_fn' field of a 'ModIface'
......@@ -1284,9 +1308,13 @@ data ModGuts
-- one); c.f. 'tcg_fam_inst_env'
mg_safe_haskell :: SafeHaskellMode, -- ^ Safe Haskell mode
mg_trust_pkg :: Bool -- ^ Do we need to trust our
mg_trust_pkg :: Bool, -- ^ Do we need to trust our
-- own package for Safe Haskell?
-- See Note [RnNames . Trust Own Package]
mg_doc_hdr :: !(Maybe HsDocString), -- ^ Module header.
mg_decl_docs :: !DeclDocMap, -- ^ Docs on declarations.
mg_arg_docs :: !ArgDocMap -- ^ Docs on arguments.
}
-- The ModGuts takes on several slightly different forms:
......
......@@ -30,6 +30,8 @@ module InteractiveEval (
exprType,
typeKind,
parseName,
getDocs,
GetDocsFailure(..),
showModule,
moduleIsBootOrNotObjectLinkable,
parseExpr, compileParsedExpr,
......@@ -91,6 +93,8 @@ import Data.Dynamic
import Data.Either
import qualified Data.IntMap as IntMap
import Data.List (find,intercalate)
import Data.Map (Map)
import qualified Data.Map as Map
import StringBuffer (stringToStringBuffer)
import Control.Monad
import GHC.Exts
......@@ -821,6 +825,70 @@ parseThing parser dflags stmt = do
Lexer.unP parser (Lexer.mkPState dflags buf loc)
getDocs :: GhcMonad m
=> Name
-> m (Either GetDocsFailure (Maybe HsDocString, Map Int HsDocString))
-- TODO: What about docs for constructors etc.?
getDocs name =
withSession $ \hsc_env -> do
case nameModule_maybe name of
Nothing -> pure (Left (NameHasNoModule name))
Just mod -> do
if isInteractiveModule mod
then pure (Left InteractiveName)
else do
ModIface { mi_doc_hdr = mb_doc_hdr
, mi_decl_docs = DeclDocMap dmap
, mi_arg_docs = ArgDocMap amap
} <- liftIO $ hscGetModuleInterface hsc_env mod
if isNothing mb_doc_hdr && Map.null dmap && Map.null amap
then pure (Left (NoDocsInIface mod compiled))
else pure (Right ( Map.lookup name dmap
, Map.findWithDefault Map.empty name amap))
where
compiled =
-- TODO: Find a more direct indicator.
case nameSrcLoc name of
RealSrcLoc {} -> False
UnhelpfulLoc {} -> True
-- | Failure modes for 'getDocs'.
-- TODO: Find a way to differentiate between modules loaded without '-haddock'
-- and modules that contain no docs.
data GetDocsFailure
-- | 'nameModule_maybe' returned 'Nothing'.
= NameHasNoModule Name
-- | This is probably because the module was loaded without @-haddock@,
-- but it's also possible that the entire module contains no documentation.
| NoDocsInIface
Module
Bool -- ^ 'True': The module was compiled.
-- 'False': The module was :loaded.
-- | The 'Name' was defined interactively.
| InteractiveName
instance Outputable GetDocsFailure where
ppr (NameHasNoModule name) =
quotes (ppr name) <+> text "has no module where we could look for docs."
ppr (NoDocsInIface mod compiled) = vcat
[ text "Can't find any documentation for" <+> ppr mod <> char '.'
, text "This is probably because the module was"
<+> text (if compiled then "compiled" else "loaded")
<+> text "without '-haddock',"
, text "but it's also possible that the module contains no documentation."
, text ""
, if compiled
then text "Try re-compiling with '-haddock'."
else text "Try running ':set -haddock' and :load the file again."
-- TODO: Figure out why :reload doesn't load the docs and maybe fix it.
]
ppr InteractiveName =
text "Docs are unavailable for interactive declarations."
-- -----------------------------------------------------------------------------
-- Getting the type of an expression
......
......@@ -234,6 +234,12 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this
maybe_rn_syntax :: forall a. a -> Maybe a ;
maybe_rn_syntax empty_val
| dopt Opt_D_dump_rn_ast dflags = Just empty_val
-- We want to serialize the documentation in the .hi-files,
-- and need to extract it from the renamed syntax first.
-- See 'ExtractDocs.extractDocs'.
| gopt Opt_Haddock dflags = Just empty_val
| keep_rn_syntax = Just empty_val
| otherwise = Nothing ;
......
......@@ -118,6 +118,12 @@ Compiler
:ghc-flag:`-fexternal-dynamic-refs`. If you don't know why you might
need this, you don't need it.
GHCi
~~~~
- Added an experimental :ghci-cmd:`:doc` command that displays the
documentation for a declaration.
Runtime system
~~~~~~~~~~~~~~
......
......@@ -2374,6 +2374,14 @@ commonly used commands.
see the number of each breakpoint). The ``*`` form deletes all the
breakpoints.
.. ghci-cmd:: :doc; ⟨name⟩
(Experimental: This command will likely change significantly in GHC 8.8.)
Displays the documentation for the given name. Currently the command is
restricted to displaying the documentation directly on the declaration
in question, ignoring documentation for arguments, constructors etc.
.. ghci-cmd:: :edit; ⟨file⟩
Opens an editor to edit the file ⟨file⟩, or the most recently loaded
......
......@@ -48,6 +48,7 @@ import GhcMonad ( modifySession )
import qualified GHC
import GHC ( LoadHowMuch(..), Target(..), TargetId(..), InteractiveImport(..),
TyThing(..), Phase, BreakIndex, Resume, SingleStep, Ghc,
GetDocsFailure(..),
getModuleGraph, handleSourceError )
import HsImpExp
import HsSyn
......@@ -99,6 +100,7 @@ import Data.List ( find, group, intercalate, intersperse, isPrefixOf, nub,
partition, sort, sortBy )
import qualified Data.Set as S
import Data.Maybe
import Data.Map (Map)
import qualified Data.Map as M
import Data.Time.LocalTime ( getZonedTime )
import Data.Time.Format ( formatTime, defaultTimeLocale )
......@@ -179,6 +181,7 @@ ghciCommands = map mkCmd [
("def", keepGoing (defineMacro False), completeExpression),
("def!", keepGoing (defineMacro True), completeExpression),
("delete", keepGoing deleteCmd, noCompletion),
("doc", keepGoing' docCmd, completeIdentifier),
("edit", keepGoing' editFile, completeFilename),
("etags", keepGoing createETagsFileCmd, completeFilename),
("force", keepGoing forceCmd, completeExpression),
......@@ -288,6 +291,7 @@ defFullHelpText =
" (!: use regex instead of line number)\n" ++
" :def <cmd> <expr> define command :<cmd> (later defined command has\n" ++
" precedence, ::<cmd> is always a builtin command)\n" ++
" :doc <name> display docs for the given name (experimental)\n" ++
" :edit <file> edit file\n" ++
" :edit edit last module\n" ++
" :etags [<file>] create tags file <file> for Emacs (default: \"TAGS\")\n" ++
......@@ -1604,6 +1608,38 @@ checkModule m = do
return True
afterLoad (successIf ok) False
-----------------------------------------------------------------------------
-- :doc
docCmd :: String -> InputT GHCi ()
docCmd "" =
throwGhcException (CmdLineError "syntax: ':doc <thing-you-want-docs-for>'")
docCmd s = do
-- TODO: Maybe also get module headers for module names
names <- GHC.parseName s
e_docss <- mapM GHC.getDocs names
sdocs <- mapM (either handleGetDocsFailure (pure . pprDocs)) e_docss
let sdocs' = vcat (intersperse (text "") sdocs)
unqual <- GHC.getPrintUnqual
dflags <- getDynFlags
(liftIO . putStrLn . showSDocForUser dflags unqual) sdocs'
-- TODO: also print arg docs.
pprDocs :: (Maybe HsDocString, Map Int HsDocString) -> SDoc
pprDocs (mb_decl_docs, _arg_docs) =
maybe
(text "<has no documentation>")
(text . unpackHDS)
mb_decl_docs
handleGetDocsFailure :: GHC.GhcMonad m => GetDocsFailure -> m SDoc
handleGetDocsFailure no_docs = do
dflags <- getDynFlags
let msg = showPpr dflags no_docs
throwGhcException $ case no_docs of
NameHasNoModule {} -> Sorry msg
NoDocsInIface {} -> InstallationError msg
InteractiveName -> ProgramError msg
-----------------------------------------------------------------------------
-- :load, :add, :reload
......
......@@ -311,8 +311,11 @@ GhcThreaded = $(if $(findstring thr,$(GhcRTSWays)),YES,NO)
#
# -O(2) is pretty desirable, otherwise no inlining of prelude
# things (incl "+") happens when compiling with this compiler
#
# -haddock is needed so the GHCi :doc command can find the boot
# library docs in the respective .hi-files
GhcLibHcOpts=-O2
GhcLibHcOpts=-O2 -haddock
# Strip local symbols from libraries? This can make the libraries smaller,
# but makes debugging somewhat more difficult. Doesn't work with all ld's.
......
......@@ -17,7 +17,7 @@ visible a = a
[3 of 3] Compiling Test ( Test.hs, Test.o )
==================== Parser ====================
"
Module : Test
Copyright : (c) Simon Marlow 2002
License : BSD-style
......@@ -28,63 +28,65 @@ visible a = a
This module illustrates & tests most of the features of Haddock.
Testing references from the description: 'T', 'f', 'g', 'Visible.visible'.
"
module Test (
<IEGroup: 1>, <IEGroup: 2>, T(..), T2, T3(..), T4(..), T5(..),
T6(..), N1(..), N2(..), N3(..), N4, N5(..), N6(..), N7(..),
<IEGroup: 2>, R(..), R1(..),
test that we can export record selectors on their own:, p, q, u,
" test that we can export record selectors on their own:", p, q, u,
<IEGroup: 1>, C(a, b), D(..), E, F(..),
Test that we can export a class method on its own:, a,
" Test that we can export a class method on its own:", a,
<IEGroup: 1>, f, g, <IEGroup: 1>, <IEDocNamed: aux1>,
<IEDocNamed: aux2>, <IEDocNamed: aux3>, <IEDocNamed: aux4>,
<IEDocNamed: aux5>, <IEDocNamed: aux6>, <IEDocNamed: aux7>,
<IEDocNamed: aux8>, <IEDocNamed: aux9>, <IEDocNamed: aux10>,
<IEDocNamed: aux11>, <IEDocNamed: aux12>,
This is some inline documentation in the export list
" This is some inline documentation in the export list
> a code block using bird-tracks
> each line must begin with > (which isn't significant unless it
> is at the beginning of the line).,
> is at the beginning of the line).",
<IEGroup: 1>, module Hidden, <IEGroup: 1>, module Visible,
nested-style doc comments , <IEGroup: 1>, Ex(..), <IEGroup: 1>, k,
l, m, o, <IEGroup: 1>, <IEGroup: 2>,
" nested-style doc comments ", <IEGroup: 1>, Ex(..), <IEGroup: 1>,
k, l, m, o, <IEGroup: 1>, <IEGroup: 2>,
"
> a literal line
$ a non /literal/ line $
, f'
", f'
) where
import Hidden
import Visible
<document comment>
data T a b
= This comment describes the 'A' constructor A Int (Maybe Float) |
This comment describes the 'B' constructor B (T a b, T Int Float)
= " This comment describes the 'A' constructor"
A Int (Maybe Float) |
" This comment describes the 'B' constructor"
B (T a b, T Int Float)
<document comment>
data T2 a b = T2 a b
<document comment>
data T3 a b = A1 a | B1 b
data T4 a b = A2 a | B2 b
data T5 a b = documents 'A3' A3 a | documents 'B3' B3 b
data T5 a b = " documents 'A3'" A3 a | " documents 'B3'" B3 b
<document comment>
data T6
= This is the doc for 'A4' A4 |
This is the doc for 'B4' B4 |
This is the doc for 'C4' C4
= " This is the doc for 'A4'" A4 |
" This is the doc for 'B4'" B4 |
" This is the doc for 'C4'" C4
<document comment>
newtype N1 a = N1 a
<document comment>
newtype N2 a b = N2 {n :: a b}
<document comment>
newtype N3 a b = N3 {n3 :: a b this is the 'n3' field}
newtype N3 a b = N3 {n3 :: a b " this is the 'n3' field"}
<document comment>
newtype N4 a b = N4 a
newtype N5 a b
= N5 {n5 :: a b no docs on the datatype or the constructor}
newtype N6 a b = docs on the constructor only N6 {n6 :: a b}
= N5 {n5 :: a b " no docs on the datatype or the constructor"}
newtype N6 a b = " docs on the constructor only" N6 {n6 :: a b}
<document comment>
newtype N7 a b = The 'N7' constructor N7 {n7 :: a b}
newtype N7 a b = " The 'N7' constructor" N7 {n7 :: a b}
class (D a) => C a where
a :: IO a
b :: [a]
......@@ -107,20 +109,20 @@ class F a where
ff :: a
<document comment>
data R
= This is the 'C1' record constructor, with the following fields:
C1 {p :: Int This comment applies to the 'p' field,
q :: forall a. a -> a This comment applies to the 'q' field,
r, s :: Int This comment applies to both 'r' and 's'} |
This is the 'C2' record constructor, also with some fields:
= " This is the 'C1' record constructor, with the following fields:"
C1 {p :: Int " This comment applies to the 'p' field",
q :: forall a. a -> a " This comment applies to the 'q' field",
r, s :: Int " This comment applies to both 'r' and 's'"} |
" This is the 'C2' record constructor, also with some fields:"
C2 {t :: T1
-> (T2 Int Int) -> (T3 Bool Bool) -> (T4 Float Float) -> T5 () (),
u, v :: Int}
<document comment>
data R1
= This is the 'C3' record constructor
C3 {s1 :: Int The 's1' record selector,
s2 :: Int The 's2' record selector,
s3 :: Int The 's3' record selector}
= " This is the 'C3' record constructor"
C3 {s1 :: Int " The 's1' record selector",
s2 :: Int " The 's2' record selector",
s3 :: Int " The 's3' record selector"}
<document comment>
<document comment>
<document comment>
......@@ -151,26 +153,27 @@ data Ex a
Ex4 (forall a. a -> a)
<document comment>
k ::
T () () This argument has type 'T'
-> (T2 Int Int) This argument has type 'T2 Int Int'
T () () " This argument has type 'T'"
-> (T2 Int Int) " This argument has type 'T2 Int Int'"
-> (T3 Bool Bool
-> T4 Float Float) This argument has type @T3 Bool Bool -> T4 Float Float@
-> T5 () () This argument has a very long description that should
-> T4 Float Float) " This argument has type @T3 Bool Bool -> T4 Float Float@"
-> T5 () () " This argument has a very long description that should
hopefully cause some wrapping to happen when it is finally
rendered by Haddock in the generated HTML page.
-> IO () This is the result type
l :: (Int, Int, Float) takes a triple -> Int returns an 'Int'
rendered by Haddock in the generated HTML page."
-> IO () " This is the result type"
l :: (Int, Int, Float) " takes a triple" -> Int " returns an 'Int'"
<document comment>
m ::