summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladislav Zavialov <vlad.z.4096@gmail.com>2019-12-05 03:06:40 +0300
committerBen Gamari <ben@smart-cactus.org>2020-07-21 14:50:01 -0400
commit19e80b9af252eee760dc047765a9930ef00067ec (patch)
treecb45fce4b1e74e1a82c5bd926fda0e92de1964c1
parent58235d46bd4e9fbf69bd82969b29cd9c6ab051e1 (diff)
downloadhaskell-19e80b9af252eee760dc047765a9930ef00067ec.tar.gz
Accumulate Haddock comments in P (#17544, #17561, #8944)
Haddock comments are, first and foremost, comments. It's very annoying to incorporate them into the grammar. We can take advantage of an important property: adding a Haddock comment does not change the parse tree in any way other than wrapping some nodes in HsDocTy and the like (and if it does, that's a bug). This patch implements the following: * Accumulate Haddock comments with their locations in the P monad. This is handled in the lexer. * After parsing, do a pass over the AST to associate Haddock comments with AST nodes using location info. * Report the leftover comments to the user as a warning (-Winvalid-haddock).
-rw-r--r--compiler/GHC/Driver/Backpack.hs1
-rw-r--r--compiler/GHC/Driver/Flags.hs1
-rw-r--r--compiler/GHC/Driver/Session.hs3
-rw-r--r--compiler/GHC/Hs.hs7
-rw-r--r--compiler/GHC/Hs/Decls.hs54
-rw-r--r--compiler/GHC/Hs/Doc.hs4
-rw-r--r--compiler/GHC/Hs/Stats.hs2
-rw-r--r--compiler/GHC/Parser.y467
-rw-r--r--compiler/GHC/Parser/Lexer.x106
-rw-r--r--compiler/GHC/Parser/PostProcess.hs214
-rw-r--r--compiler/GHC/Parser/PostProcess/Haddock.hs1565
-rw-r--r--compiler/GHC/Tc/Module.hs2
-rw-r--r--compiler/GHC/ThToHs.hs2
-rw-r--r--compiler/GHC/Types/SrcLoc.hs141
-rw-r--r--compiler/GHC/Utils/Misc.hs60
-rw-r--r--docs/users_guide/8.12.1-notes.rst44
-rw-r--r--docs/users_guide/using-warnings.rst19
-rw-r--r--testsuite/tests/ghc-api/T11579.hs9
-rw-r--r--testsuite/tests/ghc-api/T11579.stdout2
-rw-r--r--testsuite/tests/ghc-api/annotations/comments.stdout7
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/T11768.hs7
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/T11768.stderr4
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/T17544.hs56
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr1090
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.hs24
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr154
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/T8944.hs10
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/T8944.stderr11
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/all.T97
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/haddockA022.stderr20
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/haddockA030.stderr6
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/haddockA033.stderr6
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/haddockA041.hs2
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/haddockExtraDocs.hs25
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/haddockExtraDocs.stderr16
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/haddockTySyn.hs3
-rw-r--r--testsuite/tests/haddock/should_compile_flag_haddock/haddockTySyn.stderr6
-rw-r--r--testsuite/tests/parser/should_compile/DumpParsedAst.stderr2
-rw-r--r--testsuite/tests/parser/should_compile/KindSigs.stderr2
-rw-r--r--testsuite/tests/parser/should_compile/T15323.stderr2
-rw-r--r--utils/check-ppr/Main.hs28
41 files changed, 3655 insertions, 626 deletions
diff --git a/compiler/GHC/Driver/Backpack.hs b/compiler/GHC/Driver/Backpack.hs
index f798051a56..8dfd865a2b 100644
--- a/compiler/GHC/Driver/Backpack.hs
+++ b/compiler/GHC/Driver/Backpack.hs
@@ -695,6 +695,7 @@ summariseRequirement pn mod_name = do
ms_textual_imps = extra_sig_imports,
ms_parsed_mod = Just (HsParsedModule {
hpm_module = L loc (HsModule {
+ hsmodLayout = NoLayoutInfo,
hsmodName = Just (L loc mod_name),
hsmodExports = Nothing,
hsmodImports = [],
diff --git a/compiler/GHC/Driver/Flags.hs b/compiler/GHC/Driver/Flags.hs
index a827ffe315..f5f642ce46 100644
--- a/compiler/GHC/Driver/Flags.hs
+++ b/compiler/GHC/Driver/Flags.hs
@@ -496,6 +496,7 @@ data WarningFlag =
| Opt_WarnMissingSafeHaskellMode -- Since 8.10
| Opt_WarnCompatUnqualifiedImports -- Since 8.10
| Opt_WarnDerivingDefaults
+ | Opt_WarnInvalidHaddock -- Since 8.12
deriving (Eq, Show, Enum)
-- | Used when outputting warnings: if a reason is given, it is
diff --git a/compiler/GHC/Driver/Session.hs b/compiler/GHC/Driver/Session.hs
index 17e3796c3d..2982dbaefd 100644
--- a/compiler/GHC/Driver/Session.hs
+++ b/compiler/GHC/Driver/Session.hs
@@ -3450,7 +3450,8 @@ wWarningFlagsDeps = [
flagSpec "prepositive-qualified-module"
Opt_WarnPrepositiveQualifiedModule,
flagSpec "unused-packages" Opt_WarnUnusedPackages,
- flagSpec "compat-unqualified-imports" Opt_WarnCompatUnqualifiedImports
+ flagSpec "compat-unqualified-imports" Opt_WarnCompatUnqualifiedImports,
+ flagSpec "invalid-haddock" Opt_WarnInvalidHaddock
]
-- | These @-\<blah\>@ flags can all be reversed with @-no-\<blah\>@
diff --git a/compiler/GHC/Hs.hs b/compiler/GHC/Hs.hs
index 41876b8957..2512ba91cc 100644
--- a/compiler/GHC/Hs.hs
+++ b/compiler/GHC/Hs.hs
@@ -63,6 +63,9 @@ import Data.Data hiding ( Fixity )
-- All we actually declare here is the top-level structure for a module.
data HsModule
= HsModule {
+ hsmodLayout :: LayoutInfo,
+ -- ^ Layout info for the module.
+ -- For incomplete modules (e.g. the output of parseHeader), it is NoLayoutInfo.
hsmodName :: Maybe (Located ModuleName),
-- ^ @Nothing@: \"module X where\" is omitted (in which case the next
-- field is Nothing too)
@@ -116,11 +119,11 @@ deriving instance Data HsModule
instance Outputable HsModule where
- ppr (HsModule Nothing _ imports decls _ mbDoc)
+ ppr (HsModule _ Nothing _ imports decls _ mbDoc)
= pp_mb mbDoc $$ pp_nonnull imports
$$ pp_nonnull decls
- ppr (HsModule (Just name) exports imports decls deprec mbDoc)
+ ppr (HsModule _ (Just name) exports imports decls deprec mbDoc)
= vcat [
pp_mb mbDoc,
case exports of
diff --git a/compiler/GHC/Hs/Decls.hs b/compiler/GHC/Hs/Decls.hs
index c4d457d808..9759225109 100644
--- a/compiler/GHC/Hs/Decls.hs
+++ b/compiler/GHC/Hs/Decls.hs
@@ -92,6 +92,7 @@ module GHC.Hs.Decls (
HsGroup(..), emptyRdrGroup, emptyRnGroup, appendGroups, hsGroupInstDecls,
hsGroupTopLevelFixitySigs,
+ partitionBindsAndSigs,
) where
-- friends:
@@ -219,6 +220,38 @@ Template Haskell `Dec`. If there are any duplicate signatures between the two
fields, this will result in an error (#17608).
-}
+-- | Partition a list of HsDecls into function/pattern bindings, signatures,
+-- type family declarations, type family instances, and documentation comments.
+--
+-- Panics when given a declaration that cannot be put into any of the output
+-- groups.
+--
+-- The primary use of this function is to implement
+-- 'GHC.Parser.PostProcess.cvBindsAndSigs'.
+partitionBindsAndSigs
+ :: [LHsDecl GhcPs]
+ -> (LHsBinds GhcPs, [LSig GhcPs], [LFamilyDecl GhcPs],
+ [LTyFamInstDecl GhcPs], [LDataFamInstDecl GhcPs], [LDocDecl])
+partitionBindsAndSigs = go
+ where
+ go [] = (emptyBag, [], [], [], [], [])
+ go ((L l decl) : ds) =
+ let (bs, ss, ts, tfis, dfis, docs) = go ds in
+ case decl of
+ ValD _ b
+ -> (L l b `consBag` bs, ss, ts, tfis, dfis, docs)
+ SigD _ s
+ -> (bs, L l s : ss, ts, tfis, dfis, docs)
+ TyClD _ (FamDecl _ t)
+ -> (bs, ss, L l t : ts, tfis, dfis, docs)
+ InstD _ (TyFamInstD { tfid_inst = tfi })
+ -> (bs, ss, ts, L l tfi : tfis, dfis, docs)
+ InstD _ (DataFamInstD { dfid_inst = dfi })
+ -> (bs, ss, ts, tfis, L l dfi : dfis, docs)
+ DocD _ d
+ -> (bs, ss, ts, tfis, dfis, L l d : docs)
+ _ -> pprPanic "partitionBindsAndSigs" (ppr decl)
+
-- | Haskell Group
--
-- A 'HsDecl' is categorised into a 'HsGroup' before being
@@ -643,10 +676,29 @@ type instance XDataDecl GhcPs = NoExtField
type instance XDataDecl GhcRn = DataDeclRn
type instance XDataDecl GhcTc = DataDeclRn
-type instance XClassDecl GhcPs = NoExtField
+type instance XClassDecl GhcPs = LayoutInfo -- See Note [Class LayoutInfo]
type instance XClassDecl GhcRn = NameSet -- FVs
type instance XClassDecl GhcTc = NameSet -- FVs
+{- Note [Class LayoutInfo]
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+The LayoutInfo is used to associate Haddock comments with parts of the declaration.
+Compare the following examples:
+
+ class C a where
+ f :: a -> Int
+ -- ^ comment on f
+
+ class C a where
+ f :: a -> Int
+ -- ^ comment on C
+
+Notice how "comment on f" and "comment on C" differ only by indentation level.
+Thus we have to record the indentation level of the class declarations.
+
+See also Note [Adding Haddock comments to the syntax tree] in GHC.Parser.PostProcess.Haddock
+-}
+
type instance XXTyClDecl (GhcPass _) = NoExtCon
-- Simple classifiers for TyClDecl
diff --git a/compiler/GHC/Hs/Doc.hs b/compiler/GHC/Hs/Doc.hs
index 9a5035b46e..55571c5128 100644
--- a/compiler/GHC/Hs/Doc.hs
+++ b/compiler/GHC/Hs/Doc.hs
@@ -7,6 +7,7 @@ module GHC.Hs.Doc
, LHsDocString
, mkHsDocString
, mkHsDocStringUtf8ByteString
+ , isEmptyDocString
, unpackHDS
, hsDocStringToByteString
, ppr_mbDoc
@@ -64,6 +65,9 @@ instance Binary HsDocString where
instance Outputable HsDocString where
ppr = doubleQuotes . text . unpackHDS
+isEmptyDocString :: HsDocString -> Bool
+isEmptyDocString (HsDocString bs) = BS.null bs
+
mkHsDocString :: String -> HsDocString
mkHsDocString s =
inlinePerformIO $ do
diff --git a/compiler/GHC/Hs/Stats.hs b/compiler/GHC/Hs/Stats.hs
index 5b76372f37..9d7f8e8384 100644
--- a/compiler/GHC/Hs/Stats.hs
+++ b/compiler/GHC/Hs/Stats.hs
@@ -22,7 +22,7 @@ import Data.Char
-- | Source Statistics
ppSourceStats :: Bool -> Located HsModule -> SDoc
-ppSourceStats short (L _ (HsModule _ exports imports ldecls _ _))
+ppSourceStats short (L _ (HsModule{ hsmodExports = exports, hsmodImports = imports, hsmodDecls = ldecls }))
= (if short then hcat else vcat)
(map pp_val
[("ExportAll ", export_all), -- 1 if no export list
diff --git a/compiler/GHC/Parser.y b/compiler/GHC/Parser.y
index 8c9f0f8ef2..3043ba92b1 100644
--- a/compiler/GHC/Parser.y
+++ b/compiler/GHC/Parser.y
@@ -35,6 +35,7 @@ module GHC.Parser
, parseTypeSignature
, parseStmt, parseIdentifier
, parseType, parseHeader
+ , parseModuleNoHaddock
)
where
@@ -73,6 +74,7 @@ import GHC.Types.SrcLoc
import GHC.Unit.Module
import GHC.Types.Basic
import GHC.Types.ForeignCall
+import GHC.Hs.Doc
import GHC.Core.Type ( unrestrictedFunTyCon, Mult(..), Specificity(..) )
import GHC.Core.Class ( FunDep )
@@ -93,7 +95,7 @@ import GHC.Builtin.Types ( unitTyCon, unitDataCon, tupleTyCon, tupleDataCon, nil
manyDataConTyCon)
}
-%expect 234 -- shift/reduce conflicts
+%expect 232 -- shift/reduce conflicts
{- Last updated: 08 June 2020
@@ -116,16 +118,6 @@ productions around in this file.
-------------------------------------------------------------------------------
-state 0 contains 1 shift/reduce conflicts.
-
- Conflicts: DOCNEXT (empty missing_module_keyword reduces)
-
-Ambiguity when the source file starts with "-- | doc". We need another
-token of lookahead to determine if a top declaration or the 'module' keyword
-follows. Shift parses as if the 'module' keyword follows.
-
--------------------------------------------------------------------------------
-
state 60 contains 1 shift/reduce conflict.
context -> btype .
@@ -607,11 +599,6 @@ are the most common patterns, rewritten as regular expressions for clarity:
PRIMFLOAT { L _ (ITprimfloat _) }
PRIMDOUBLE { L _ (ITprimdouble _) }
- DOCNEXT { L _ (ITdocCommentNext _) }
- DOCPREV { L _ (ITdocCommentPrev _) }
- DOCNAMED { L _ (ITdocCommentNamed _) }
- DOCSECTION { L _ (ITdocSection _ _) }
-
-- Template Haskell
'[|' { L _ (ITopenExpQuote _ _) }
'[p|' { L _ ITopenPatQuote }
@@ -633,7 +620,7 @@ TH_QQUASIQUOTE { L _ (ITqQuasiQuote _) }
%tokentype { (Located Token) }
-- Exported parsers
-%name parseModule module
+%name parseModuleNoHaddock module
%name parseSignature signature
%name parseImport importdecl
%name parseStatement e_stmt
@@ -742,27 +729,25 @@ unitdecls :: { OrdList (LHsUnitDecl PackageName) }
| unitdecl { unitOL $1 }
unitdecl :: { LHsUnitDecl PackageName }
- : maybedocheader 'module' maybe_src modid maybemodwarning maybeexports 'where' body
+ : 'module' maybe_src modid maybemodwarning maybeexports 'where' body
-- XXX not accurate
- { sL1 $2 $ DeclD
- (case snd $3 of
+ { sL1 $1 $ DeclD
+ (case snd $2 of
NotBoot -> HsSrcFile
IsBoot -> HsBootFile)
- $4
- (Just $ sL1 $2 (HsModule (Just $4) $6 (fst $ snd $8) (snd $ snd $8) $5 $1)) }
- | maybedocheader 'signature' modid maybemodwarning maybeexports 'where' body
- { sL1 $2 $ DeclD
- HsigFile
$3
- (Just $ sL1 $2 (HsModule (Just $3) $5 (fst $ snd $7) (snd $ snd $7) $4 $1)) }
- -- NB: MUST have maybedocheader here, otherwise shift-reduce conflict
- -- will prevent us from parsing both forms.
- | maybedocheader 'module' maybe_src modid
- { sL1 $2 $ DeclD (case snd $3 of
+ (Just $ sL1 $1 (HsModule (thdOf3 $7) (Just $3) $5 (fst $ sndOf3 $7) (snd $ sndOf3 $7) $4 Nothing)) }
+ | 'signature' modid maybemodwarning maybeexports 'where' body
+ { sL1 $1 $ DeclD
+ HsigFile
+ $2
+ (Just $ sL1 $1 (HsModule (thdOf3 $6) (Just $2) $4 (fst $ sndOf3 $6) (snd $ sndOf3 $6) $3 Nothing)) }
+ | 'module' maybe_src modid
+ { sL1 $1 $ DeclD (case snd $2 of
NotBoot -> HsSrcFile
- IsBoot -> HsBootFile) $4 Nothing }
- | maybedocheader 'signature' modid
- { sL1 $2 $ DeclD HsigFile $3 Nothing }
+ IsBoot -> HsBootFile) $3 Nothing }
+ | 'signature' modid
+ { sL1 $1 $ DeclD HsigFile $2 Nothing }
| 'dependency' unitid mayberns
{ sL1 $1 $ IncludeD (IncludeDecl { idUnitId = $2
, idModRenaming = $3
@@ -783,29 +768,25 @@ unitdecl :: { LHsUnitDecl PackageName }
-- know what they are doing. :-)
signature :: { Located HsModule }
- : maybedocheader 'signature' modid maybemodwarning maybeexports 'where' body
+ : 'signature' modid maybemodwarning maybeexports 'where' body
{% fileSrcSpan >>= \ loc ->
- ams (L loc (HsModule (Just $3) $5 (fst $ snd $7)
- (snd $ snd $7) $4 $1)
+ ams (L loc (HsModule (thdOf3 $6) (Just $2) $4 (fst $ sndOf3 $6)
+ (snd $ sndOf3 $6) $3 Nothing)
)
- ([mj AnnSignature $2, mj AnnWhere $6] ++ fst $7) }
+ ([mj AnnSignature $1, mj AnnWhere $5] ++ fstOf3 $6) }
module :: { Located HsModule }
- : maybedocheader 'module' modid maybemodwarning maybeexports 'where' body
+ : 'module' modid maybemodwarning maybeexports 'where' body
{% fileSrcSpan >>= \ loc ->
- ams (L loc (HsModule (Just $3) $5 (fst $ snd $7)
- (snd $ snd $7) $4 $1)
+ ams (L loc (HsModule (thdOf3 $6) (Just $2) $4 (fst $ sndOf3 $6)
+ (snd $ sndOf3 $6) $3 Nothing)
)
- ([mj AnnModule $2, mj AnnWhere $6] ++ fst $7) }
+ ([mj AnnModule $1, mj AnnWhere $5] ++ fstOf3 $6) }
| body2
{% fileSrcSpan >>= \ loc ->
- ams (L loc (HsModule Nothing Nothing
- (fst $ snd $1) (snd $ snd $1) Nothing Nothing))
- (fst $1) }
-
-maybedocheader :: { Maybe LHsDocString }
- : moduleheader { $1 }
- | {- empty -} { Nothing }
+ ams (L loc (HsModule (thdOf3 $1) Nothing Nothing
+ (fst $ sndOf3 $1) (snd $ sndOf3 $1) Nothing Nothing))
+ (fstOf3 $1) }
missing_module_keyword :: { () }
: {- empty -} {% pushModuleContext }
@@ -823,16 +804,18 @@ maybemodwarning :: { Maybe (Located WarningTxt) }
| {- empty -} { Nothing }
body :: { ([AddAnn]
- ,([LImportDecl GhcPs], [LHsDecl GhcPs])) }
+ ,([LImportDecl GhcPs], [LHsDecl GhcPs])
+ ,LayoutInfo) }
: '{' top '}' { (moc $1:mcc $3:(fst $2)
- , snd $2) }
- | vocurly top close { (fst $2, snd $2) }
+ , snd $2, ExplicitBraces) }
+ | vocurly top close { (fst $2, snd $2, VirtualBraces (getVOCURLY $1)) }
body2 :: { ([AddAnn]
- ,([LImportDecl GhcPs], [LHsDecl GhcPs])) }
+ ,([LImportDecl GhcPs], [LHsDecl GhcPs])
+ ,LayoutInfo) }
: '{' top '}' { (moc $1:mcc $3
- :(fst $2), snd $2) }
- | missing_module_keyword top close { ([],snd $2) }
+ :(fst $2), snd $2, ExplicitBraces) }
+ | missing_module_keyword top close { ([],snd $2, VirtualBraces leftmostColumn) }
top :: { ([AddAnn]
@@ -848,17 +831,17 @@ top1 :: { ([LImportDecl GhcPs], [LHsDecl GhcPs]) }
-- Module declaration & imports only
header :: { Located HsModule }
- : maybedocheader 'module' modid maybemodwarning maybeexports 'where' header_body
+ : 'module' modid maybemodwarning maybeexports 'where' header_body
{% fileSrcSpan >>= \ loc ->
- ams (L loc (HsModule (Just $3) $5 $7 [] $4 $1
- )) [mj AnnModule $2,mj AnnWhere $6] }
- | maybedocheader 'signature' modid maybemodwarning maybeexports 'where' header_body
+ ams (L loc (HsModule NoLayoutInfo (Just $2) $4 $6 [] $3 Nothing
+ )) [mj AnnModule $1,mj AnnWhere $5] }
+ | 'signature' modid maybemodwarning maybeexports 'where' header_body
{% fileSrcSpan >>= \ loc ->
- ams (L loc (HsModule (Just $3) $5 $7 [] $4 $1
- )) [mj AnnModule $2,mj AnnWhere $6] }
+ ams (L loc (HsModule NoLayoutInfo (Just $2) $4 $6 [] $3 Nothing
+ )) [mj AnnModule $1,mj AnnWhere $5] }
| header_body2
{% fileSrcSpan >>= \ loc ->
- return (L loc (HsModule Nothing Nothing $1 [] Nothing
+ return (L loc (HsModule NoLayoutInfo Nothing Nothing $1 [] Nothing
Nothing)) }
header_body :: { [LImportDecl GhcPs] }
@@ -885,26 +868,18 @@ maybeexports :: { (Maybe (Located [LIE GhcPs])) }
| {- empty -} { Nothing }
exportlist :: { OrdList (LIE GhcPs) }
- : expdoclist ',' expdoclist {% addAnnotation (oll $1) AnnComma (gl $2)
- >> return ($1 `appOL` $3) }
- | exportlist1 { $1 }
-
-exportlist1 :: { OrdList (LIE GhcPs) }
- : expdoclist export expdoclist ',' exportlist1
- {% (addAnnotation (oll ($1 `appOL` $2 `appOL` $3))
- AnnComma (gl $4) ) >>
- return ($1 `appOL` $2 `appOL` $3 `appOL` $5) }
- | expdoclist export expdoclist { $1 `appOL` $2 `appOL` $3 }
- | expdoclist { $1 }
+ : exportlist1 { $1 }
+ | {- empty -} { nilOL }
-expdoclist :: { OrdList (LIE GhcPs) }
- : exp_doc expdoclist { $1 `appOL` $2 }
- | {- empty -} { nilOL }
+ -- trailing comma:
+ | exportlist1 ',' { $1 }
+ | ',' { nilOL }
-exp_doc :: { OrdList (LIE GhcPs) }
- : docsection { unitOL (sL1 $1 (case (unLoc $1) of (n, doc) -> IEGroup noExtField n doc)) }
- | docnamed { unitOL (sL1 $1 (IEDocNamed noExtField ((fst . unLoc) $1))) }
- | docnext { unitOL (sL1 $1 (IEDoc noExtField (unLoc $1))) }
+exportlist1 :: { OrdList (LIE GhcPs) }
+ : exportlist1 ',' export
+ {% (addAnnotation (oll $1) AnnComma (gl $2) ) >>
+ return ($1 `appOL` $3) }
+ | export { $1 }
-- No longer allow things like [] and (,,,) to be exported
@@ -1112,15 +1087,15 @@ topdecl :: { LHsDecl GhcPs }
--
cl_decl :: { LTyClDecl GhcPs }
: 'class' tycl_hdr fds where_cls
- {% amms (mkClassDecl (comb4 $1 $2 $3 $4) $2 $3 (snd $ unLoc $4))
- (mj AnnClass $1:(fst $ unLoc $3)++(fst $ unLoc $4)) }
+ {% amms (mkClassDecl (comb4 $1 $2 $3 $4) $2 $3 (sndOf3 $ unLoc $4) (thdOf3 $ unLoc $4))
+ (mj AnnClass $1:(fst $ unLoc $3)++(fstOf3 $ unLoc $4)) }
-- Type declarations (toplevel)
--
ty_decl :: { LTyClDecl GhcPs }
-- ordinary type synonyms
- : 'type' type '=' ktypedoc
- -- Note ktypedoc, not sigtype, on the right of '='
+ : 'type' type '=' ktype
+ -- Note ktype, not sigtype, on the right of '='
-- We allow an explicit for-all but we don't insert one
-- in type Foo a = (b,b)
-- Instead we just say b is out of scope
@@ -1168,7 +1143,7 @@ ty_decl :: { LTyClDecl GhcPs }
-- standalone kind signature
standalone_kind_sig :: { LStandaloneKindSig GhcPs }
- : 'type' sks_vars '::' ktypedoc
+ : 'type' sks_vars '::' ktype
{% amms (mkStandaloneKindSig (comb2 $1 $4) $2 $4)
[mj AnnType $1,mu AnnDcolon $3] }
@@ -1538,7 +1513,7 @@ where_decls :: { Located ([AddAnn]
,sL1 $3 (snd $ unLoc $3)) }
pattern_synonym_sig :: { LSig GhcPs }
- : 'pattern' con_list '::' sigtypedoc
+ : 'pattern' con_list '::' sigtype
{% ams (sLL $1 $> $ PatSynSig noExtField (unLoc $2) (mkLHsSigType $4))
[mj AnnPattern $1, mu AnnDcolon $3] }
@@ -1552,7 +1527,7 @@ decl_cls : at_decl_cls { $1 }
| decl { $1 }
-- A 'default' signature used with the generic-programming extension
- | 'default' infixexp '::' sigtypedoc
+ | 'default' infixexp '::' sigtype
{% runECP_P $2 >>= \ $2 ->
do { v <- checkValSigLhs $2
; let err = text "in default signature" <> colon <+>
@@ -1577,20 +1552,23 @@ decls_cls :: { Located ([AddAnn],OrdList (LHsDecl GhcPs)) } -- Reversed
decllist_cls
:: { Located ([AddAnn]
- , OrdList (LHsDecl GhcPs)) } -- Reversed
+ , OrdList (LHsDecl GhcPs)
+ , LayoutInfo) } -- Reversed
: '{' decls_cls '}' { sLL $1 $> (moc $1:mcc $3:(fst $ unLoc $2)
- ,snd $ unLoc $2) }
- | vocurly decls_cls close { $2 }
+ ,snd $ unLoc $2, ExplicitBraces) }
+ | vocurly decls_cls close { let { L l (anns, decls) = $2 }
+ in L l (anns, decls, VirtualBraces (getVOCURLY $1)) }
-- Class body
--
where_cls :: { Located ([AddAnn]
- ,(OrdList (LHsDecl GhcPs))) } -- Reversed
+ ,(OrdList (LHsDecl GhcPs)) -- Reversed
+ ,LayoutInfo) }
-- No implicit parameters
-- May have type declarations
- : 'where' decllist_cls { sLL $1 $> (mj AnnWhere $1:(fst $ unLoc $2)
- ,snd $ unLoc $2) }
- | {- empty -} { noLoc ([],nilOL) }
+ : 'where' decllist_cls { sLL $1 $> (mj AnnWhere $1:(fstOf3 $ unLoc $2)
+ ,sndOf3 $ unLoc $2,thdOf3 $ unLoc $2) }
+ | {- empty -} { noLoc ([],nilOL,NoLayoutInfo) }
-- Declarations in instance bodies
--
@@ -1869,10 +1847,10 @@ safety :: { Located Safety }
fspec :: { Located ([AddAnn]
,(Located StringLiteral, Located RdrName, LHsSigType GhcPs)) }
- : STRING var '::' sigtypedoc { sLL $1 $> ([mu AnnDcolon $3]
+ : STRING var '::' sigtype { sLL $1 $> ([mu AnnDcolon $3]
,(L (getLoc $1)
(getStringLiteral $1), $2, mkLHsSigType $4)) }
- | var '::' sigtypedoc { sLL $1 $> ([mu AnnDcolon $2]
+ | var '::' sigtype { sLL $1 $> ([mu AnnDcolon $2]
,(noLoc (StringLiteral NoSourceText nilFS), $1, mkLHsSigType $3)) }
-- if the entity string is missing, it defaults to the empty string;
-- the meaning of an empty entity string depends on the calling
@@ -1892,10 +1870,6 @@ opt_tyconsig :: { ([AddAnn], Maybe (Located RdrName)) }
sigtype :: { LHsType GhcPs }
: ctype { $1 }
-sigtypedoc :: { LHsType GhcPs }
- : ctypedoc { $1 }
-
-
sig_vars :: { Located [Located RdrName] } -- Returned in reversed order
: sig_vars ',' var {% addAnnotation (gl $ head $ unLoc $1)
AnnComma (gl $2)
@@ -1925,17 +1899,12 @@ forall_telescope :: { Located ([AddAnn], HsForAllTelescope GhcPs) }
( [mu AnnForall $1, mu AnnRarrow $3]
, mkHsForAllVisTele req_tvbs ) }}
--- A ktype/ktypedoc is a ctype/ctypedoc, possibly with a kind annotation
+-- A ktype is a ctype, possibly with a kind annotation
ktype :: { LHsType GhcPs }
: ctype { $1 }
| ctype '::' kind {% ams (sLL $1 $> $ HsKindSig noExtField $1 $3)
[mu AnnDcolon $2] }
-ktypedoc :: { LHsType GhcPs }
- : ctypedoc { $1 }
- | ctypedoc '::' kind {% ams (sLL $1 $> $ HsKindSig noExtField $1 $3)
- [mu AnnDcolon $2] }
-
-- A ctype is a for-all type
ctype :: { LHsType GhcPs }
: forall_telescope ctype {% let (forall_anns, forall_tele) = unLoc $1 in
@@ -1953,33 +1922,6 @@ ctype :: { LHsType GhcPs }
[mu AnnDcolon $2] }
| type { $1 }
--- Note [ctype and ctypedoc]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- It would have been nice to simplify the grammar by unifying `ctype` and
--- ctypedoc` into one production, allowing comments on types everywhere (and
--- rejecting them after parsing, where necessary). This is however not possible
--- since it leads to ambiguity. The reason is the support for comments on record
--- fields:
--- data R = R { field :: Int -- ^ comment on the field }
--- If we allow comments on types here, it's not clear if the comment applies
--- to 'field' or to 'Int'. So we must use `ctype` to describe the type.
-
-ctypedoc :: { LHsType GhcPs }
- : forall_telescope ctypedoc {% let (forall_anns, forall_tele) = unLoc $1 in
- ams (sLL $1 $> $
- HsForAllTy { hst_tele = forall_tele
- , hst_xforall = noExtField
- , hst_body = $2 })
- forall_anns }
- | context '=>' ctypedoc {% addAnnotation (gl $1) (toUnicodeAnn AnnDarrow $2) (gl $2)
- >> return (sLL $1 $> $
- HsQualTy { hst_ctxt = $1
- , hst_xqual = noExtField
- , hst_body = $3 }) }
- | ipvar '::' type {% ams (sLL $1 $> (HsIParamTy noExtField $1 $3))
- [mu AnnDcolon $2] }
- | typedoc { $1 }
-
----------------------
-- Notes for 'context'
-- We parse a context as a btype so that we don't get reduce/reduce
@@ -1995,21 +1937,11 @@ context :: { LHsContext GhcPs }
; ams ctx anns
} }
--- See Note [Constr variations of non-terminals]
-constr_context :: { LHsContext GhcPs }
- : constr_btype {% do { (anns,ctx) <- checkContext $1
- ; if null (unLoc ctx)
- then addAnnotation (gl $1) AnnUnit (gl $1)
- else return ()
- ; ams ctx anns
- } }
-
{- Note [GADT decl discards annotations]
~~~~~~~~~~~~~~~~~~~~~
The type production for
- btype `->` ctypedoc
- btype docprev `->` ctypedoc
+ btype `->` ctype
add the AnnRarrow annotation twice, in different places.
@@ -2035,53 +1967,12 @@ mult :: { LHsType GhcPs }
: btype { $1 }
-typedoc :: { LHsType GhcPs }
- : btype { $1 }
- | btype docprev { sLL $1 $> $ HsDocTy noExtField $1 $2 }
- | docnext btype { sLL $1 $> $ HsDocTy noExtField $2 $1 }
- | btype '->' ctypedoc {% ams $1 [mu AnnRarrow $2] -- See note [GADT decl discards annotations]
- >> ams (sLL $1 $> $ HsFunTy noExtField HsUnrestrictedArrow $1 $3)
- [mu AnnRarrow $2] }
- | btype docprev '->' ctypedoc {% ams $1 [mu AnnRarrow $3] -- See note [GADT decl discards annotations]
- >> ams (sLL $1 $> $
- HsFunTy noExtField HsUnrestrictedArrow
- (L (comb2 $1 $2) (HsDocTy noExtField $1 $2)) $4)
- [mu AnnRarrow $3] }
- | btype '#->' ctypedoc {% hintLinear (getLoc $2) >>
- ams (sLL $1 $> $ HsFunTy noExtField HsLinearArrow $1 $3)
- [mu AnnRarrow $2] }
- | btype docprev '#->' ctypedoc {% hintLinear (getLoc $2) >>
- ams (sLL $1 $> $
- HsFunTy noExtField HsLinearArrow
- (L (comb2 $1 $2) (HsDocTy noExtField $1 $2)) $4)
- [mu AnnRarrow $3] }
- | docnext btype '->' ctypedoc {% ams $2 [mu AnnRarrow $3] -- See note [GADT decl discards annotations]
- >> ams (sLL $1 $> $
- HsFunTy noExtField HsUnrestrictedArrow
- (L (comb2 $1 $2) (HsDocTy noExtField $2 $1))
- $4)
- [mu AnnRarrow $3] }
-
--- See Note [Constr variations of non-terminals]
-constr_btype :: { LHsType GhcPs }
- : constr_tyapps {% mergeOps (unLoc $1) }
-
--- See Note [Constr variations of non-terminals]
-constr_tyapps :: { Located [Located TyEl] } -- NB: This list is reversed
- : constr_tyapp { sL1 $1 [$1] }
- | constr_tyapps constr_tyapp { sLL $1 $> $ $2 : (unLoc $1) }
-
--- See Note [Constr variations of non-terminals]
-constr_tyapp :: { Located TyEl }
- : tyapp { $1 }
- | docprev { sL1 $1 $ TyElDocPrev (unLoc $1) }
-
btype :: { LHsType GhcPs }
- : tyapps {% mergeOps $1 }
+ : tyapps {% mergeOps (unLoc $1) }
-tyapps :: { [Located TyEl] } -- NB: This list is reversed
- : tyapp { [$1] }
- | tyapps tyapp { $2 : $1 }
+tyapps :: { Located [Located TyEl] } -- NB: This list is reversed
+ : tyapp { sL1 $1 [$1] }
+ | tyapps tyapp { sLL $1 $> $ $2 : unLoc $1 }
tyapp :: { Located TyEl }
: atype { sL1 $1 $ TyElOpd (unLoc $1) }
@@ -2162,9 +2053,9 @@ inst_type :: { LHsSigType GhcPs }
: sigtype { mkLHsSigType $1 }
deriv_types :: { [LHsSigType GhcPs] }
- : ktypedoc { [mkLHsSigType $1] }
+ : ktype { [mkLHsSigType $1] }
- | ktypedoc ',' deriv_types {% addAnnotation (gl $1) AnnComma (gl $2)
+ | ktype ',' deriv_types {% addAnnotation (gl $1) AnnComma (gl $2)
>> return (mkLHsSigType $1 : $3) }
comma_types0 :: { [LHsType GhcPs] } -- Zero or more: ty,ty,ty
@@ -2266,10 +2157,10 @@ gadt_constrlist :: { Located ([AddAnn]
| {- empty -} { noLoc ([],[]) }
gadt_constrs :: { Located [LConDecl GhcPs] }
- : gadt_constr_with_doc ';' gadt_constrs
+ : gadt_constr ';' gadt_constrs
{% addAnnotation (gl $1) AnnSemi (gl $2)
>> return (L (comb2 $1 $3) ($1 : unLoc $3)) }
- | gadt_constr_with_doc { L (gl $1) [$1] }
+ | gadt_constr { L (gl $1) [$1] }
| {- empty -} { noLoc [] }
-- We allow the following forms:
@@ -2278,19 +2169,12 @@ gadt_constrs :: { Located [LConDecl GhcPs] }
-- D { x,y :: a } :: T a
-- forall a. Eq a => D { x,y :: a } :: T a
-gadt_constr_with_doc :: { LConDecl GhcPs }
-gadt_constr_with_doc
- : maybe_docnext ';' gadt_constr
- {% return $ addConDoc $3 $1 }
- | gadt_constr
- {% return $1 }
-
gadt_constr :: { LConDecl GhcPs }
-- see Note [Difference in parsing GADT and data constructors]
-- Returns a list because of: C,D :: ty
- : con_list '::' sigtypedoc
- {% ams (sLL $1 $> (mkGadtDecl (unLoc $1) $3))
- [mu AnnDcolon $2] }
+ : optSemi con_list '::' sigtype
+ {% ams (sLL $2 $> (mkGadtDecl (unLoc $2) $4))
+ [mu AnnDcolon $3] }
{- Note [Difference in parsing GADT and data constructors]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2305,91 +2189,36 @@ allowed in usual data constructors, but not in GADTs).
-}
constrs :: { Located ([AddAnn],[LConDecl GhcPs]) }
- : maybe_docnext '=' constrs1 { L (comb2 $2 $3) ([mj AnnEqual $2]
- ,addConDocs (unLoc $3) $1)}
+ : '=' constrs1 { sLL $1 $2 ([mj AnnEqual $1],unLoc $2)}
constrs1 :: { Located [LConDecl GhcPs] }
- : constrs1 maybe_docnext '|' maybe_docprev constr
- {% addAnnotation (gl $ head $ unLoc $1) AnnVbar (gl $3)
- >> return (sLL $1 $> (addConDoc $5 $2 : addConDocFirst (unLoc $1) $4)) }
+ : constrs1 '|' constr
+ {% addAnnotation (gl $ head $ unLoc $1) AnnVbar (gl $2)
+ >> return (sLL $1 $> ($3 : unLoc $1)) }
| constr { sL1 $1 [$1] }
-{- Note [Constr variations of non-terminals]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In record declarations we assume that 'ctype' used to parse the type will not
-consume the trailing docprev:
-
- data R = R { field :: Int -- ^ comment on the field }
-
-In 'R' we expect the comment to apply to the entire field, not to 'Int'. The
-same issue is detailed in Note [ctype and ctypedoc].
-
-So, we do not want 'ctype' to consume 'docprev', therefore
- we do not want 'btype' to consume 'docprev', therefore
- we do not want 'tyapps' to consume 'docprev'.
-
-At the same time, when parsing a 'constr', we do want to consume 'docprev':
-
- data T = C Int -- ^ comment on Int
- Bool -- ^ comment on Bool
-
-So, we do want 'constr_stuff' to consume 'docprev'.
-
-The problem arises because the clauses in 'constr' have the following
-structure:
-
- (a) context '=>' constr_stuff (e.g. data T a = Ord a => C a)
- (b) constr_stuff (e.g. data T a = C a)
-
-and to avoid a reduce/reduce conflict, 'context' and 'constr_stuff' must be
-compatible. And for 'context' to be compatible with 'constr_stuff', it must
-consume 'docprev'.
-
-So, we want 'context' to consume 'docprev', therefore
- we want 'btype' to consume 'docprev', therefore
- we want 'tyapps' to consume 'docprev'.
-
-Our requirements end up conflicting: for parsing record types, we want 'tyapps'
-to leave 'docprev' alone, but for parsing constructors, we want it to consume
-'docprev'.
-
-As the result, we maintain two parallel hierarchies of non-terminals that
-either consume 'docprev' or not:
-
- tyapps constr_tyapps
- btype constr_btype
- context constr_context
- ...
-
-They must be kept identical except for their treatment of 'docprev'.
-
--}
-
constr :: { LConDecl GhcPs }
- : maybe_docnext forall constr_context '=>' constr_stuff
- {% ams (let (con,details,doc_prev) = unLoc $5 in
- addConDoc (L (comb4 $2 $3 $4 $5) (mkConDeclH98 con
- (snd $ unLoc $2)
- (Just $3)
- details))
- ($1 `mplus` doc_prev))
- (mu AnnDarrow $4:(fst $ unLoc $2)) }
- | maybe_docnext forall constr_stuff
- {% ams ( let (con,details,doc_prev) = unLoc $3 in
- addConDoc (L (comb2 $2 $3) (mkConDeclH98 con
- (snd $ unLoc $2)
- Nothing -- No context
- details))
- ($1 `mplus` doc_prev))
- (fst $ unLoc $2) }
+ : forall context '=>' constr_stuff
+ {% ams (let (con,details) = unLoc $4 in
+ (L (comb4 $1 $2 $3 $4) (mkConDeclH98 con
+ (snd $ unLoc $1)
+ (Just $2)
+ details)))
+ (mu AnnDarrow $3:(fst $ unLoc $1)) }
+ | forall constr_stuff
+ {% ams (let (con,details) = unLoc $2 in
+ (L (comb2 $1 $2) (mkConDeclH98 con
+ (snd $ unLoc $1)
+ Nothing -- No context
+ details)))
+ (fst $ unLoc $1) }
forall :: { Located ([AddAnn], Maybe [LHsTyVarBndr Specificity GhcPs]) }
: 'forall' tv_bndrs '.' { sLL $1 $> ([mu AnnForall $1,mj AnnDot $3], Just $2) }
| {- empty -} { noLoc ([], Nothing) }
-constr_stuff :: { Located (Located RdrName, HsConDeclDetails GhcPs, Maybe LHsDocString) }
- : constr_tyapps {% do { c <- mergeDataCon (unLoc $1)
+constr_stuff :: { Located (Located RdrName, HsConDeclDetails GhcPs) }
+ : tyapps {% do { c <- mergeDataCon (unLoc $1)
; return $ sL1 $1 c } }
fielddecls :: { [LConDeclField GhcPs] }
@@ -2397,17 +2226,17 @@ fielddecls :: { [LConDeclField GhcPs] }
| fielddecls1 { $1 }
fielddecls1 :: { [LConDeclField GhcPs] }
- : fielddecl maybe_docnext ',' maybe_docprev fielddecls1
- {% addAnnotation (gl $1) AnnComma (gl $3) >>
- return ((addFieldDoc $1 $4) : addFieldDocs $5 $2) }
+ : fielddecl ',' fielddecls1
+ {% addAnnotation (gl $1) AnnComma (gl $2) >>
+ return ($1 : $3) }
| fielddecl { [$1] }
fielddecl :: { LConDeclField GhcPs }
-- A list because of f,g :: Int
- : maybe_docnext sig_vars '::' ctype maybe_docprev
- {% ams (L (comb2 $2 $4)
- (ConDeclField noExtField (reverse (map (\ln@(L l n) -> L l $ FieldOcc noExtField ln) (unLoc $2))) $4 ($1 `mplus` $5)))
- [mu AnnDcolon $3] }
+ : sig_vars '::' ctype
+ {% ams (L (comb2 $1 $3)
+ (ConDeclField noExtField (reverse (map (\ln@(L l n) -> L l $ FieldOcc noExtField ln) (unLoc $1))) $3 Nothing))
+ [mu AnnDcolon $2] }
-- Reversed!
maybe_derivings :: { HsDeriving GhcPs }
@@ -2438,7 +2267,8 @@ deriving :: { LHsDerivingClause GhcPs }
[mj AnnDeriving $1] }
deriv_clause_types :: { Located [LHsSigType GhcPs] }
- : qtycondoc { sL1 $1 [mkLHsSigType $1] }
+ : qtycon { let { tc = sL1 $1 (HsTyVar noExtField NotPromoted $1) } in
+ sL1 $1 [mkLHsSigType tc] }
| '(' ')' {% ams (sLL $1 $> [])
[mop $1,mcp $2] }
| '(' deriv_types ')' {% ams (sLL $1 $> $2)
@@ -2471,15 +2301,6 @@ There's an awkward overlap with a type signature. Consider
We can't tell whether to reduce var to qvar until after we've read the signatures.
-}
-docdecl :: { LHsDecl GhcPs }
- : docdecld { sL1 $1 (DocD noExtField (unLoc $1)) }
-
-docdecld :: { LDocDecl }
- : docnext { sL1 $1 (DocCommentNext (unLoc $1)) }
- | docprev { sL1 $1 (DocCommentPrev (unLoc $1)) }
- | docnamed { sL1 $1 (case (unLoc $1) of (n, doc) -> DocCommentNamed n doc) }
- | docsection { sL1 $1 (case (unLoc $1) of (n, doc) -> DocGroup n doc) }
-
decl_no_th :: { LHsDecl GhcPs }
: sigdecl { $1 }
@@ -2497,7 +2318,6 @@ decl_no_th :: { LHsDecl GhcPs }
_ <- amsL l (ann ++ (fst $ unLoc $3));
return $! (sL l $ ValD noExtField r) } }
| pattern_synonym_decl { $1 }
- | docdecl { $1 }
decl :: { LHsDecl GhcPs }
: decl_no_th { $1 }
@@ -2529,14 +2349,14 @@ gdrh :: { LGRHS GhcPs (LHsExpr GhcPs) }
sigdecl :: { LHsDecl GhcPs }
:
-- See Note [Declaration/signature overlap] for why we need infixexp here
- infixexp '::' sigtypedoc
+ infixexp '::' sigtype
{% do { $1 <- runECP_P $1
; v <- checkValSigLhs $1
; _ <- amsL (comb2 $1 $>) [mu AnnDcolon $2]
; return (sLL $1 $> $ SigD noExtField $
TypeSig noExtField [v] (mkLHsSigWcType $3))} }
- | var ',' sig_vars '::' sigtypedoc
+ | var ',' sig_vars '::' sigtype
{% do { let sig = TypeSig noExtField ($1 : reverse (unLoc $3))
(mkLHsSigWcType $5)
; addAnnotation (gl $1) AnnComma (gl $2)
@@ -3581,10 +3401,6 @@ qtycon :: { Located RdrName } -- Qualified or unqualified
: QCONID { sL1 $1 $! mkQual tcClsName (getQCONID $1) }
| tycon { $1 }
-qtycondoc :: { LHsType GhcPs } -- Qualified or unqualified
- : qtycon { sL1 $1 (HsTyVar noExtField NotPromoted $1) }
- | qtycon docprev { sLL $1 $> (HsDocTy noExtField (sL1 $1 (HsTyVar noExtField NotPromoted $1)) $2) }
-
tycon :: { Located RdrName } -- Unqualified
: CONID { sL1 $1 $! mkUnqual tcClsName (getCONID $1) }
@@ -3824,37 +3640,6 @@ bars :: { ([SrcSpan],Int) } -- One or more bars
: bars '|' { ((fst $1)++[gl $2],snd $1 + 1) }
| '|' { ([gl $1],1) }
------------------------------------------------------------------------------
--- Documentation comments
-
-docnext :: { LHsDocString }
- : DOCNEXT {% return (sL1 $1 (mkHsDocString (getDOCNEXT $1))) }
-
-docprev :: { LHsDocString }
- : DOCPREV {% return (sL1 $1 (mkHsDocString (getDOCPREV $1))) }
-
-docnamed :: { Located (String, HsDocString) }
- : DOCNAMED {%
- let string = getDOCNAMED $1
- (name, rest) = break isSpace string
- in return (sL1 $1 (name, mkHsDocString rest)) }
-
-docsection :: { Located (Int, HsDocString) }
- : DOCSECTION {% let (n, doc) = getDOCSECTION $1 in
- return (sL1 $1 (n, mkHsDocString doc)) }
-
-moduleheader :: { Maybe LHsDocString }
- : DOCNEXT {% let string = getDOCNEXT $1 in
- return (Just (sL1 $1 (mkHsDocString string))) }
-
-maybe_docprev :: { Maybe LHsDocString }
- : docprev { Just $1 }
- | {- empty -} { Nothing }
-
-maybe_docnext :: { Maybe LHsDocString }
- : docnext { Just $1 }
- | {- empty -} { Nothing }
-
{
happyError :: P a
happyError = srcParseFail
@@ -3885,11 +3670,7 @@ getINLINE (L _ (ITinline_prag _ inl conl)) = (inl,conl)
getSPEC_INLINE (L _ (ITspec_inline_prag _ True)) = (Inline, FunLike)
getSPEC_INLINE (L _ (ITspec_inline_prag _ False)) = (NoInline,FunLike)
getCOMPLETE_PRAGs (L _ (ITcomplete_prag x)) = x
-
-getDOCNEXT (L _ (ITdocCommentNext x)) = x
-getDOCPREV (L _ (ITdocCommentPrev x)) = x
-getDOCNAMED (L _ (ITdocCommentNamed x)) = x
-getDOCSECTION (L _ (ITdocSection n x)) = (n, x)
+getVOCURLY (L (RealSrcSpan l _) ITvocurly) = srcSpanStartCol l
getINTEGERs (L _ (ITinteger (IL src _ _))) = src
getCHARs (L _ (ITchar src _)) = src
@@ -4209,4 +3990,16 @@ oll l =
asl :: [Located a] -> Located b -> Located a -> P ()
asl [] (L ls _) (L l _) = addAnnotation l AnnSemi ls
asl (x:_xs) (L ls _) _x = addAnnotation (getLoc x) AnnSemi ls
+
+-- | Parse a Haskell module with Haddock comments.
+-- This is done in two steps:
+--
+-- * 'parseModuleNoHaddock' to build the AST
+-- * 'addHaddockToModule' to insert Haddock comments into it
+--
+-- This is the only parser entry point that deals with Haddock comments.
+-- The other entry points ('parseDeclaration', 'parseExpression', etc) do
+-- not insert them into the AST.
+parseModule :: P (Located HsModule)
+parseModule = parseModuleNoHaddock >>= addHaddockToModule
}
diff --git a/compiler/GHC/Parser/Lexer.x b/compiler/GHC/Parser/Lexer.x
index ef9f1803bf..7265e1dffb 100644
--- a/compiler/GHC/Parser/Lexer.x
+++ b/compiler/GHC/Parser/Lexer.x
@@ -66,7 +66,8 @@ module GHC.Parser.Lexer (
lexTokenStream,
AddAnn(..),mkParensApiAnn,
addAnnsAt,
- commentToAnnotation
+ commentToAnnotation,
+ HdkComment(..),
) where
import GHC.Prelude
@@ -97,6 +98,8 @@ import GHC.Utils.Outputable
import GHC.Data.StringBuffer
import GHC.Data.FastString
import GHC.Types.Unique.FM
+import GHC.Data.Maybe
+import GHC.Data.OrdList
import GHC.Utils.Misc ( readRational, readHexRational )
-- compiler/main
@@ -109,6 +112,7 @@ import GHC.Unit
import GHC.Types.Basic ( InlineSpec(..), RuleMatchInfo(..),
IntegralLit(..), FractionalLit(..),
SourceText(..) )
+import GHC.Hs.Doc
-- compiler/parser
import GHC.Parser.CharClass
@@ -363,10 +367,8 @@ $tab { warnTab }
-- Haddock comments
-<0,option_prags> {
- "-- " $docsym / { ifExtension HaddockBit } { multiline_doc_comment }
- "{-" \ ? $docsym / { ifExtension HaddockBit } { nested_doc_comment }
-}
+"-- " $docsym / { ifExtension HaddockBit } { multiline_doc_comment }
+"{-" \ ? $docsym / { ifExtension HaddockBit } { nested_doc_comment }
-- "special" symbols
@@ -1271,11 +1273,8 @@ nested_comment cont span buf len = do
go (reverse $ lexemeToString buf len) (1::Int) input
where
go commentAcc 0 input = do
- setInput input
- b <- getBit RawTokenStreamBit
- if b
- then docCommentEnd input commentAcc ITblockComment buf span
- else cont
+ let finalizeComment str = (Nothing, ITblockComment str)
+ commentEnd cont input commentAcc finalizeComment buf span
go commentAcc n input = case alexGetChar' input of
Nothing -> errBrace input (psRealSpan span)
Just ('-',input) -> case alexGetChar' input of
@@ -1365,24 +1364,37 @@ return control to parseNestedPragma by returning the ITcomment_line_prag token.
See #314 for more background on the bug this fixes.
-}
-withLexedDocType :: (AlexInput -> (String -> Token) -> Bool -> P (PsLocated Token))
+withLexedDocType :: (AlexInput -> (String -> (HdkComment, Token)) -> Bool -> P (PsLocated Token))
-> P (PsLocated Token)
withLexedDocType lexDocComment = do
input@(AI _ buf) <- getInput
case prevChar buf ' ' of
-- The `Bool` argument to lexDocComment signals whether or not the next
-- line of input might also belong to this doc comment.
- '|' -> lexDocComment input ITdocCommentNext True
- '^' -> lexDocComment input ITdocCommentPrev True
- '$' -> lexDocComment input ITdocCommentNamed True
+ '|' -> lexDocComment input mkHdkCommentNext True
+ '^' -> lexDocComment input mkHdkCommentPrev True
+ '$' -> lexDocComment input mkHdkCommentNamed True
'*' -> lexDocSection 1 input
_ -> panic "withLexedDocType: Bad doc type"
where
lexDocSection n input = case alexGetChar' input of
Just ('*', input) -> lexDocSection (n+1) input
- Just (_, _) -> lexDocComment input (ITdocSection n) False
+ Just (_, _) -> lexDocComment input (mkHdkCommentSection n) False
Nothing -> do setInput input; lexToken -- eof reached, lex it normally
+mkHdkCommentNext, mkHdkCommentPrev :: String -> (HdkComment, Token)
+mkHdkCommentNext str = (HdkCommentNext (mkHsDocString str), ITdocCommentNext str)
+mkHdkCommentPrev str = (HdkCommentPrev (mkHsDocString str), ITdocCommentPrev str)
+
+mkHdkCommentNamed :: String -> (HdkComment, Token)
+mkHdkCommentNamed str =
+ let (name, rest) = break isSpace str
+ in (HdkCommentNamed name (mkHsDocString rest), ITdocCommentNamed str)
+
+mkHdkCommentSection :: Int -> String -> (HdkComment, Token)
+mkHdkCommentSection n str =
+ (HdkCommentSection n (mkHsDocString str), ITdocSection n str)
+
-- RULES pragmas turn on the forall and '.' keywords, and we turn them
-- off again at the end of the pragma.
rulePrag :: Action
@@ -1425,17 +1437,34 @@ endPrag span _buf _len = do
-- it writes the wrong token length to the parser state. This function is
-- called afterwards, so it can just update the state.
-docCommentEnd :: AlexInput -> String -> (String -> Token) -> StringBuffer ->
- PsSpan -> P (PsLocated Token)
-docCommentEnd input commentAcc docType buf span = do
+commentEnd :: P (PsLocated Token)
+ -> AlexInput
+ -> String
+ -> (String -> (Maybe HdkComment, Token))
+ -> StringBuffer
+ -> PsSpan
+ -> P (PsLocated Token)
+commentEnd cont input commentAcc finalizeComment buf span = do
setInput input
let (AI loc nextBuf) = input
comment = reverse commentAcc
span' = mkPsSpan (psSpanStart span) loc
last_len = byteDiff buf nextBuf
-
span `seq` setLastToken span' last_len
- return (L span' (docType comment))
+ let (m_hdk_comment, hdk_token) = finalizeComment comment
+ whenIsJust m_hdk_comment $ \hdk_comment ->
+ P $ \s -> POk (s {hdk_comments = hdk_comments s `snocOL` L span' hdk_comment}) ()
+ b <- getBit RawTokenStreamBit
+ if b then return (L span' hdk_token)
+ else cont
+
+docCommentEnd :: AlexInput -> String -> (String -> (HdkComment, Token)) -> StringBuffer ->
+ PsSpan -> P (PsLocated Token)
+docCommentEnd input commentAcc docType buf span = do
+ let finalizeComment str =
+ let (hdk_comment, token) = docType str
+ in (Just hdk_comment, token)
+ commentEnd lexToken input commentAcc finalizeComment buf span
errBrace :: AlexInput -> RealSrcSpan -> P a
errBrace (AI end _) span = failLocMsgP (realSrcSpanStart span) (psRealLoc end) "unterminated `{-'"
@@ -2170,6 +2199,15 @@ data ParserFlags = ParserFlags {
, pExtsBitmap :: !ExtsBitmap -- ^ bitmap of permitted extensions
}
+-- | Haddock comment as produced by the lexer. These are accumulated in
+-- 'PState' and then processed in "GHC.Parser.PostProcess.Haddock".
+data HdkComment
+ = HdkCommentNext HsDocString
+ | HdkCommentPrev HsDocString
+ | HdkCommentNamed String HsDocString
+ | HdkCommentSection Int HsDocString
+ deriving Show
+
data PState = PState {
buffer :: StringBuffer,
options :: ParserFlags,
@@ -2211,7 +2249,13 @@ data PState = PState {
annotations :: [(ApiAnnKey,[RealSrcSpan])],
eof_pos :: Maybe RealSrcSpan,
comment_q :: [RealLocated AnnotationComment],
- annotations_comments :: [(RealSrcSpan,[RealLocated AnnotationComment])]
+ annotations_comments :: [(RealSrcSpan,[RealLocated AnnotationComment])],
+
+ -- Haddock comments accumulated in ascending order of their location
+ -- (BufPos). We use OrdList to get O(1) snoc.
+ --
+ -- See Note [Adding Haddock comments to the syntax tree] in GHC.Parser.PostProcess.Haddock
+ hdk_comments :: OrdList (PsLocated HdkComment)
}
-- last_loc and last_len are used when generating error messages,
-- and in pushCurrentContext only. Sigh, if only Happy passed the
@@ -2698,7 +2742,8 @@ mkPStatePure options buf loc =
annotations = [],
eof_pos = Nothing,
comment_q = [],
- annotations_comments = []
+ annotations_comments = [],
+ hdk_comments = nilOL
}
where init_loc = PsLoc loc (BufPos 0)
@@ -2917,10 +2962,6 @@ lexer queueComments cont = do
(L span tok) <- lexTokenFun
--trace ("token: " ++ show tok) $ do
- if (queueComments && isDocComment tok)
- then queueComment (L (psRealSpan span) tok)
- else return ()
-
if (queueComments && isComment tok)
then queueComment (L (psRealSpan span) tok) >> lexer queueComments cont
else cont (L (mkSrcSpanPs span) tok)
@@ -3372,13 +3413,10 @@ commentToAnnotation _ = panic "commentToAnnotation"
isComment :: Token -> Bool
isComment (ITlineComment _) = True
isComment (ITblockComment _) = True
+isComment (ITdocCommentNext _) = True
+isComment (ITdocCommentPrev _) = True
+isComment (ITdocCommentNamed _) = True
+isComment (ITdocSection _ _) = True
+isComment (ITdocOptions _) = True
isComment _ = False
-
-isDocComment :: Token -> Bool
-isDocComment (ITdocCommentNext _) = True
-isDocComment (ITdocCommentPrev _) = True
-isDocComment (ITdocCommentNamed _) = True
-isDocComment (ITdocSection _ _) = True
-isDocComment (ITdocOptions _) = True
-isDocComment _ = False
}
diff --git a/compiler/GHC/Parser/PostProcess.hs b/compiler/GHC/Parser/PostProcess.hs
index 398bd78ddc..3cf5b30b06 100644
--- a/compiler/GHC/Parser/PostProcess.hs
+++ b/compiler/GHC/Parser/PostProcess.hs
@@ -126,7 +126,6 @@ import GHC.Builtin.Names ( allNameStrings )
import GHC.Types.SrcLoc
import GHC.Types.Unique ( hasKey )
import GHC.Data.OrdList ( OrdList, fromOL )
-import GHC.Data.Bag ( emptyBag, consBag )
import GHC.Utils.Outputable as Outputable
import GHC.Data.FastString
import GHC.Data.Maybe
@@ -172,16 +171,18 @@ mkClassDecl :: SrcSpan
-> Located (Maybe (LHsContext GhcPs), LHsType GhcPs)
-> Located (a,[LHsFunDep GhcPs])
-> OrdList (LHsDecl GhcPs)
+ -> LayoutInfo
-> P (LTyClDecl GhcPs)
-mkClassDecl loc (L _ (mcxt, tycl_hdr)) fds where_cls
+mkClassDecl loc (L _ (mcxt, tycl_hdr)) fds where_cls layoutInfo
= do { (binds, sigs, ats, at_defs, _, docs) <- cvBindsAndSigs where_cls
; let cxt = fromMaybe (noLoc []) mcxt
; (cls, tparams, fixity, ann) <- checkTyClHdr True tycl_hdr
; addAnnsAt loc ann -- Add any API Annotations to the top SrcSpan
; (tyvars,annst) <- checkTyVars (text "class") whereDots cls tparams
; addAnnsAt loc annst -- Add any API Annotations to the top SrcSpan
- ; return (L loc (ClassDecl { tcdCExt = noExtField, tcdCtxt = cxt
+ ; return (L loc (ClassDecl { tcdCExt = layoutInfo
+ , tcdCtxt = cxt
, tcdLName = cls, tcdTyVars = tyvars
, tcdFixity = fixity
, tcdFDs = snd (unLoc fds)
@@ -418,14 +419,7 @@ fromSpecTyVarBndr bndr = case bndr of
-- | Groups together bindings for a single function
cvTopDecls :: OrdList (LHsDecl GhcPs) -> [LHsDecl GhcPs]
-cvTopDecls decls = go (fromOL decls)
- where
- go :: [LHsDecl GhcPs] -> [LHsDecl GhcPs]
- go [] = []
- go ((L l (ValD x b)) : ds)
- = L l' (ValD x b') : go ds'
- where (L l' b', ds') = getMonoBind (L l b) ds
- go (d : ds) = d : go ds
+cvTopDecls decls = getMonoBindAll (fromOL decls)
-- Declaration list may only contain value bindings and signatures.
cvBindGroup :: OrdList (LHsDecl GhcPs) -> P (HsValBinds GhcPs)
@@ -441,33 +435,32 @@ cvBindsAndSigs :: OrdList (LHsDecl GhcPs)
-- Input decls contain just value bindings and signatures
-- and in case of class or instance declarations also
-- associated type declarations. They might also contain Haddock comments.
-cvBindsAndSigs fb = go (fromOL fb)
+cvBindsAndSigs fb = do
+ fb' <- drop_bad_decls (fromOL fb)
+ return (partitionBindsAndSigs (getMonoBindAll fb'))
where
- go [] = return (emptyBag, [], [], [], [], [])
- go ((L l (ValD _ b)) : ds)
- = do { (bs, ss, ts, tfis, dfis, docs) <- go ds'
- ; return (b' `consBag` bs, ss, ts, tfis, dfis, docs) }
- where
- (b', ds') = getMonoBind (L l b) ds
- go ((L l decl) : ds)
- = do { (bs, ss, ts, tfis, dfis, docs) <- go ds
- ; case decl of
- SigD _ s
- -> return (bs, L l s : ss, ts, tfis, dfis, docs)
- TyClD _ (FamDecl _ t)
- -> return (bs, ss, L l t : ts, tfis, dfis, docs)
- InstD _ (TyFamInstD { tfid_inst = tfi })
- -> return (bs, ss, ts, L l tfi : tfis, dfis, docs)
- InstD _ (DataFamInstD { dfid_inst = dfi })
- -> return (bs, ss, ts, tfis, L l dfi : dfis, docs)
- DocD _ d
- -> return (bs, ss, ts, tfis, dfis, L l d : docs)
- SpliceD _ d
- -> addFatalError l $
- hang (text "Declaration splices are allowed only" <+>
- text "at the top level:")
- 2 (ppr d)
- _ -> pprPanic "cvBindsAndSigs" (ppr decl) }
+ -- cvBindsAndSigs is called in several places in the parser,
+ -- and its items can be produced by various productions:
+ --
+ -- * decl (when parsing a where clause or a let-expression)
+ -- * decl_inst (when parsing an instance declaration)
+ -- * decl_cls (when parsing a class declaration)
+ --
+ -- partitionBindsAndSigs can handle almost all declaration forms produced
+ -- by the aforementioned productions, except for SpliceD, which we filter
+ -- out here (in drop_bad_decls).
+ --
+ -- We're not concerned with every declaration form possible, such as those
+ -- produced by the topdecl parser production, because cvBindsAndSigs is not
+ -- called on top-level declarations.
+ drop_bad_decls [] = return []
+ drop_bad_decls (L l (SpliceD _ d) : ds) = do
+ addError l $
+ hang (text "Declaration splices are allowed only" <+>
+ text "at the top level:")
+ 2 (ppr d)
+ drop_bad_decls ds
+ drop_bad_decls (d:ds) = (d:) <$> drop_bad_decls ds
-----------------------------------------------------------------------------
-- Group function bindings into equation groups
@@ -512,6 +505,14 @@ getMonoBind (L loc1 (FunBind { fun_id = fun_id1@(L _ f1)
getMonoBind bind binds = (bind, binds)
+-- Group together adjacent FunBinds for every function.
+getMonoBindAll :: [LHsDecl GhcPs] -> [LHsDecl GhcPs]
+getMonoBindAll [] = []
+getMonoBindAll (L l (ValD _ b) : ds) =
+ let (L l' b', ds') = getMonoBind (L l b) ds
+ in L l' (ValD noExtField b') : getMonoBindAll ds'
+getMonoBindAll (d : ds) = d : getMonoBindAll ds
+
has_args :: [LMatch GhcPs (LHsExpr GhcPs)] -> Bool
has_args [] = panic "GHC.Parser.PostProcess.has_args"
has_args (L _ (Match { m_pats = args }) : _) = not (null args)
@@ -1035,21 +1036,7 @@ checkContext (L l orig_t)
else (anns ++ mkParensApiAnn lp1)
-- no need for anns, returning original
- check _anns t = checkNoDocs msg t *> return ([],L l [L l orig_t])
-
- msg = text "data constructor context"
-
--- | Check recursively if there are any 'HsDocTy's in the given type.
--- This only works on a subset of types produced by 'btype_no_ops'
-checkNoDocs :: SDoc -> LHsType GhcPs -> P ()
-checkNoDocs msg ty = go ty
- where
- go (L _ (HsAppKindTy _ ty ki)) = go ty *> go ki
- go (L _ (HsAppTy _ t1 t2)) = go t1 *> go t2
- go (L l (HsDocTy _ t ds)) = addError l $ hsep
- [ text "Unexpected haddock", quotes (ppr ds)
- , text "on", msg, quotes (ppr t) ]
- go _ = pure ()
+ check _anns _t = return ([],L l [L l orig_t])
checkImportDecl :: Maybe (Located Token)
-> Maybe (Located Token)
@@ -1338,7 +1325,6 @@ data TyEl = TyElOpr RdrName | TyElOpd (HsType GhcPs)
| TyElKindApp SrcSpan (LHsType GhcPs)
-- See Note [TyElKindApp SrcSpan interpretation]
| TyElUnpackedness ([AddAnn], SourceText, SrcUnpackedness)
- | TyElDocPrev HsDocString
{- Note [TyElKindApp SrcSpan interpretation]
@@ -1360,7 +1346,6 @@ instance Outputable TyEl where
ppr (TyElOpd ty) = ppr ty
ppr (TyElKindApp _ ki) = text "@" <> ppr ki
ppr (TyElUnpackedness (_, _, unpk)) = ppr unpk
- ppr (TyElDocPrev doc) = ppr doc
-- | Extract a strictness/unpackedness annotation from the front of a reversed
-- 'TyEl' list.
@@ -1447,11 +1432,6 @@ mergeOps all_xs = go (0 :: Int) [] id all_xs
-- See Note [Impossible case in mergeOps clause [unpk]]
panic "mergeOps.UNPACK: impossible position"
- -- clause [doc]:
- -- we do not expect to encounter any docs
- go _ _ _ ((L l (TyElDocPrev _)):_) =
- failOpDocPrev l
-
-- clause [opr]:
-- when we encounter an operator, we must have accumulated
-- something for its rhs, and there must be something left
@@ -1571,13 +1551,6 @@ pLHsTypeArg (L l (TyElOpd a)) = Just (HsValArg (L l a))
pLHsTypeArg (L _ (TyElKindApp l a)) = Just (HsTypeArg l a)
pLHsTypeArg _ = Nothing
-pDocPrev :: [Located TyEl] -> (Maybe LHsDocString, [Located TyEl])
-pDocPrev = go Nothing
- where
- go mTrailingDoc ((L l (TyElDocPrev doc)):xs) =
- go (mTrailingDoc `mplus` Just (L l doc)) xs
- go mTrailingDoc xs = (mTrailingDoc, xs)
-
orErr :: Maybe a -> b -> Either b a
orErr (Just a) _ = Right a
orErr Nothing b = Left b
@@ -1594,123 +1567,77 @@ mergeDataCon
:: [Located TyEl]
-> P ( Located RdrName -- constructor name
, HsConDeclDetails GhcPs -- constructor field information
- , Maybe LHsDocString -- docstring to go on the constructor
)
mergeDataCon all_xs =
do { (addAnns, a) <- eitherToP res
; addAnns
; return a }
where
- -- We start by splitting off the trailing documentation comment,
- -- if any exists.
- (mTrailingDoc, all_xs') = pDocPrev all_xs
-
- -- Determine whether the trailing documentation comment exists and is the
- -- only docstring in this constructor declaration.
- --
- -- When true, it means that it applies to the constructor itself:
- -- data T = C
- -- A
- -- B -- ^ Comment on C (singleDoc == True)
- --
- -- When false, it means that it applies to the last field:
- -- data T = C -- ^ Comment on C
- -- A -- ^ Comment on A
- -- B -- ^ Comment on B (singleDoc == False)
- singleDoc = isJust mTrailingDoc &&
- null [ () | (L _ (TyElDocPrev _)) <- all_xs' ]
-
-- The result of merging the list of reversed TyEl into a
-- data constructor, along with [AddAnn].
- res = goFirst all_xs'
-
- -- Take the trailing docstring into account when interpreting
- -- the docstring near the constructor.
- --
- -- data T = C -- ^ docstring right after C
- -- A
- -- B -- ^ trailing docstring
- --
- -- 'mkConDoc' must be applied to the docstring right after C, so that it
- -- falls back to the trailing docstring when appropriate (see singleDoc).
- mkConDoc mDoc | singleDoc = mDoc `mplus` mTrailingDoc
- | otherwise = mDoc
-
- -- The docstring for the last field of a data constructor.
- trailingFieldDoc | singleDoc = Nothing
- | otherwise = mTrailingDoc
+ res = goFirst all_xs
goFirst [ L l (TyElOpd (HsTyVar _ _ (L _ tc))) ]
= do { data_con <- tyConToDataCon l tc
- ; return (pure (), (data_con, PrefixCon [], mTrailingDoc)) }
+ ; return (pure (), (data_con, PrefixCon [])) }
goFirst ((L l (TyElOpd (HsRecTy _ fields))):xs)
- | (mConDoc, xs') <- pDocPrev xs
- , [ L l' (TyElOpd (HsTyVar _ _ (L _ tc))) ] <- xs'
+ | [ L l' (TyElOpd (HsTyVar _ _ (L _ tc))) ] <- xs
= do { data_con <- tyConToDataCon l' tc
- ; let mDoc = mTrailingDoc `mplus` mConDoc
- ; return (pure (), (data_con, RecCon (L l fields), mDoc)) }
+ ; return (pure (), (data_con, RecCon (L l fields))) }
goFirst [L l (TyElOpd (HsTupleTy _ HsBoxedOrConstraintTuple ts))]
= return ( pure ()
, ( L l (getRdrName (tupleDataCon Boxed (length ts)))
- , PrefixCon (map hsLinear ts)
- , mTrailingDoc ) )
+ , PrefixCon (map hsLinear ts) ) )
goFirst ((L l (TyElOpd t)):xs)
| (_, t', addAnns, xs') <- pBangTy (L l t) xs
- = go addAnns Nothing [mkLHsDocTyMaybe t' trailingFieldDoc] xs'
+ = go addAnns [t'] xs'
goFirst (L l (TyElKindApp _ _):_)
= goInfix Monoid.<> Left (l, kindAppErr)
goFirst xs
- = go (pure ()) mTrailingDoc [] xs
+ = go (pure ()) [] xs
- go addAnns mLastDoc ts [ L l (TyElOpd (HsTyVar _ _ (L _ tc))) ]
+ go addAnns ts [ L l (TyElOpd (HsTyVar _ _ (L _ tc))) ]
= do { data_con <- tyConToDataCon l tc
- ; return (addAnns, (data_con, PrefixCon (map hsLinear ts), mkConDoc mLastDoc)) }
- go addAnns mLastDoc ts ((L l (TyElDocPrev doc)):xs) =
- go addAnns (mLastDoc `mplus` Just (L l doc)) ts xs
- go addAnns mLastDoc ts ((L l (TyElOpd t)):xs)
+ ; return (addAnns, (data_con, PrefixCon (map hsLinear ts))) }
+ go addAnns ts ((L l (TyElOpd t)):xs)
| (_, t', addAnns', xs') <- pBangTy (L l t) xs
- , t'' <- mkLHsDocTyMaybe t' mLastDoc
- = go (addAnns >> addAnns') Nothing (t'':ts) xs'
- go _ _ _ ((L _ (TyElOpr _)):_) =
+ = go (addAnns >> addAnns') (t':ts) xs'
+ go _ _ ((L _ (TyElOpr _)):_) =
-- Encountered an operator: backtrack to the beginning and attempt
-- to parse as an infix definition.
goInfix
- go _ _ _ (L l (TyElKindApp _ _):_) = goInfix Monoid.<> Left (l, kindAppErr)
- go _ _ _ _ = Left malformedErr
+ go _ _ (L l (TyElKindApp _ _):_) = goInfix Monoid.<> Left (l, kindAppErr)
+ go _ _ _ = Left malformedErr
where
malformedErr =
- ( foldr combineSrcSpans noSrcSpan (map getLoc all_xs')
+ ( foldr combineSrcSpans noSrcSpan (map getLoc all_xs)
, text "Cannot parse data constructor" <+>
text "in a data/newtype declaration:" $$
- nest 2 (hsep . reverse $ map ppr all_xs'))
+ nest 2 (hsep . reverse $ map ppr all_xs))
goInfix =
- do { let xs0 = all_xs'
- ; (rhs_t, rhs_addAnns, xs1) <- pInfixSide xs0 `orErr` malformedErr
- ; let (mOpDoc, xs2) = pDocPrev xs1
- ; (op, xs3) <- case xs2 of
+ do { let xs0 = all_xs
+ ; (rhs, rhs_addAnns, xs1) <- pInfixSide xs0 `orErr` malformedErr
+ ; (op, xs3) <- case xs1 of
(L l (TyElOpr op)) : xs3 ->
do { data_con <- tyConToDataCon l op
; return (data_con, xs3) }
_ -> Left malformedErr
- ; let (mLhsDoc, xs4) = pDocPrev xs3
- ; (lhs_t, lhs_addAnns, xs5) <- pInfixSide xs4 `orErr` malformedErr
+ ; (lhs, lhs_addAnns, xs5) <- pInfixSide xs3 `orErr` malformedErr
; unless (null xs5) (Left malformedErr)
- ; let rhs = mkLHsDocTyMaybe rhs_t trailingFieldDoc
- lhs = mkLHsDocTyMaybe lhs_t mLhsDoc
- addAnns = lhs_addAnns >> rhs_addAnns
- ; return (addAnns, (op, InfixCon (hsLinear lhs) (hsLinear rhs), mkConDoc mOpDoc)) }
+ ; let addAnns = lhs_addAnns >> rhs_addAnns
+ ; return (addAnns, (op, InfixCon (hsLinear lhs) (hsLinear rhs))) }
where
malformedErr =
- ( foldr combineSrcSpans noSrcSpan (map getLoc all_xs')
+ ( foldr combineSrcSpans noSrcSpan (map getLoc all_xs)
, text "Cannot parse an infix data constructor" <+>
text "in a data/newtype declaration:" $$
- nest 2 (hsep . reverse $ map ppr all_xs'))
+ nest 2 (hsep . reverse $ map ppr all_xs))
kindAppErr =
text "Unexpected kind application" <+>
text "in a data/newtype declaration:" $$
- nest 2 (hsep . reverse $ map ppr all_xs')
+ nest 2 (hsep . reverse $ map ppr all_xs)
---------------------------------------------------------------------------
-- | Check for monad comprehensions
@@ -2902,11 +2829,6 @@ failOpFewArgs (L loc op) =
where
too_few = text "Operator applied to too few arguments:" <+> ppr op
-failOpDocPrev :: SrcSpan -> P a
-failOpDocPrev loc = addFatalError loc msg
- where
- msg = text "Unexpected documentation comment."
-
-----------------------------------------------------------------------------
-- Misc utils
@@ -3140,14 +3062,6 @@ mkLHsOpTy x op y =
let loc = getLoc x `combineSrcSpans` getLoc op `combineSrcSpans` getLoc y
in L loc (mkHsOpTy x op y)
-mkLHsDocTy :: LHsType GhcPs -> LHsDocString -> LHsType GhcPs
-mkLHsDocTy t doc =
- let loc = getLoc t `combineSrcSpans` getLoc doc
- in L loc (HsDocTy noExtField t doc)
-
-mkLHsDocTyMaybe :: LHsType GhcPs -> Maybe LHsDocString -> LHsType GhcPs
-mkLHsDocTyMaybe t = maybe t (mkLHsDocTy t)
-
-----------------------------------------------------------------------------
-- Token symbols
diff --git a/compiler/GHC/Parser/PostProcess/Haddock.hs b/compiler/GHC/Parser/PostProcess/Haddock.hs
index 409b0c120f..e109fada55 100644
--- a/compiler/GHC/Parser/PostProcess/Haddock.hs
+++ b/compiler/GHC/Parser/PostProcess/Haddock.hs
@@ -1,39 +1,1554 @@
-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE ApplicativeDo #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE DerivingVia #-}
-module GHC.Parser.PostProcess.Haddock where
+{- | This module implements 'addHaddockToModule', which inserts Haddock
+ comments accumulated during parsing into the AST (#17544).
-import GHC.Prelude
+We process Haddock comments in two phases:
+
+1. Parse the program (via the Happy parser in `Parser.y`), generating
+ an AST, and (quite separately) a list of all the Haddock comments
+ found in the file. More precisely, the Haddock comments are
+ accumulated in the `hdk_comments` field of the `PState`, the parser
+ state (see Lexer.x):
+
+ data PState = PState { ...
+ , hdk_comments :: [PsLocated HdkComment] }
+
+ Each of these Haddock comments has a `PsSpan`, which gives the `BufPos` of
+ the beginning and end of the Haddock comment.
+
+2. Walk over the AST, attaching the Haddock comments to the correct
+ parts of the tree. This step is called `addHaddockToModule`, and is
+ implemented in this module.
+
+ See Note [Adding Haddock comments to the syntax tree].
+
+This approach codifies an important principle:
+
+ The presence or absence of a Haddock comment should never change the parsing
+ of a program.
+
+Alternative approaches that did not work properly:
+
+1. Using 'RealSrcLoc' instead of 'BufPos'. This led to failures in presence
+ of {-# LANGUAGE CPP #-} and other sources of line pragmas. See documentation
+ on 'BufPos' (in GHC.Types.SrcLoc) for the details.
+
+2. In earlier versions of GHC, the Haddock comments were incorporated into the
+ Parser.y grammar. The parser constructed the AST and attached comments to it in
+ a single pass. See Note [Old solution: Haddock in the grammar] for the details.
+-}
+module GHC.Parser.PostProcess.Haddock (addHaddockToModule) where
+
+import GHC.Prelude hiding (mod)
import GHC.Hs
import GHC.Types.SrcLoc
+import GHC.Driver.Session ( WarningFlag(..) )
+import GHC.Utils.Outputable hiding ( (<>) )
+import GHC.Data.Bag
+import Data.Semigroup
+import Data.Foldable
+import Data.Traversable
+import Data.Maybe
import Control.Monad
+import Control.Monad.Trans.State.Strict
+import Control.Monad.Trans.Reader
+import Control.Monad.Trans.Writer
+import Data.Functor.Identity
+import Data.Coerce
+import qualified Data.Monoid
+
+import GHC.Parser.Lexer
+import GHC.Utils.Misc (mergeListsBy, filterOut, mapLastM, (<&&>))
+
+{- Note [Adding Haddock comments to the syntax tree]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+'addHaddock' traverses the AST in concrete syntax order, building a computation
+(represented by HdkA) that reconstructs the AST but with Haddock comments
+inserted in appropriate positions:
+
+ addHaddock :: HasHaddock a => a -> HdkA a
+
+Consider this code example:
+
+ f :: Int -- ^ comment on argument
+ -> Bool -- ^ comment on result
+
+In the AST, the "Int" part of this snippet is represented like this
+(pseudo-code):
+
+ L (BufSpan 6 8) (HsTyVar "Int") :: LHsType GhcPs
+
+And the comments are represented like this (pseudo-code):
+
+ L (BufSpan 11 35) (HdkCommentPrev "comment on argument")
+ L (BufSpan 46 69) (HdkCommentPrev "comment on result")
+
+So when we are traversing the AST and 'addHaddock' is applied to HsTyVar "Int",
+how does it know to associate it with "comment on argument" but not with
+"comment on result"?
+
+The trick is to look in the space between syntactic elements. In the example above,
+the location range in which we search for HdkCommentPrev is as follows:
+
+ f :: Int████████████████████████
+ ████Bool -- ^ comment on result
+
+We search for comments after HsTyVar "Int" and until the next syntactic
+element, in this case HsTyVar "Bool".
+
+Ignoring the "->" allows us to accomodate alternative coding styles:
+
+ f :: Int -> -- ^ comment on argument
+ Bool -- ^ comment on result
+
+Sometimes we also need to take indentation information into account.
+Compare the following examples:
+
+ class C a where
+ f :: a -> Int
+ -- ^ comment on f
+
+ class C a where
+ f :: a -> Int
+ -- ^ comment on C
+
+Notice how "comment on f" and "comment on C" differ only by indentation level.
+
+Therefore, in order to know the location range in which the comments are applicable
+to a syntactic elements, we need three nuggets of information:
+ 1. lower bound on the BufPos of a comment
+ 2. upper bound on the BufPos of a comment
+ 3. minimum indentation level of a comment
+
+This information is represented by the 'LocRange' type.
+
+In order to propagate this information, we have the 'HdkA' applicative.
+'HdkA' is defined as follows:
+
+ data HdkA a = HdkA (Maybe BufSpan) (HdkM a)
+
+The first field contains a 'BufSpan', which represents the location
+span taken by a syntactic element:
+
+ addHaddock (L bufSpan ...) = HdkA (Just bufSpan) ...
+
+The second field, 'HdkM', is a stateful computation that looks up Haddock
+comments in the specified location range:
+
+ HdkM a ≈
+ LocRange -- The allowed location range
+ -> [PsLocated HdkComment] -- Unallocated comments
+ -> (a, -- AST with comments inserted into it
+ [PsLocated HdkComment]) -- Leftover comments
+
+The 'Applicative' instance for 'HdkA' is defined in such a way that the
+location range of every computation is defined by its neighbours:
+
+ addHaddock aaa <*> addHaddock bbb <*> addHaddock ccc
+
+Here, the 'LocRange' passed to the 'HdkM' computation of addHaddock bbb
+is determined by the BufSpan recorded in addHaddock aaa and addHaddock ccc.
+
+This is why it's important to traverse the AST in the order of the concrete
+syntax. In the example above we assume that aaa, bbb, ccc are ordered by location:
+
+ * getBufSpan (getLoc aaa) < getBufSpan (getLoc bbb)
+ * getBufSpan (getLoc bbb) < getBufSpan (getLoc ccc)
+
+Violation of this assumption would lead to bugs, and care must be taken to
+traverse the AST correctly. For example, when dealing with class declarations,
+we have to use 'flattenBindsAndSigs' to traverse it in the correct order.
+-}
+
+-- | Add Haddock documentation accumulated in the parser state
+-- to a parsed HsModule.
+--
+-- Reports badly positioned comments when -Winvalid-haddock is enabled.
+addHaddockToModule :: Located HsModule -> P (Located HsModule)
+addHaddockToModule lmod = do
+ pState <- getPState
+ let all_comments = toList (hdk_comments pState)
+ initial_hdk_st = HdkSt all_comments []
+ (lmod', final_hdk_st) = runHdkA (addHaddock lmod) initial_hdk_st
+ hdk_warnings = collectHdkWarnings final_hdk_st
+ -- lmod': module with Haddock comments inserted into the AST
+ -- hdk_warnings: warnings accumulated during AST/comment processing
+ mapM_ reportHdkWarning hdk_warnings
+ return lmod'
+
+reportHdkWarning :: HdkWarn -> P ()
+reportHdkWarning (HdkWarnInvalidComment (L l _)) =
+ addWarning Opt_WarnInvalidHaddock (mkSrcSpanPs l) $
+ text "A Haddock comment cannot appear in this position and will be ignored."
+reportHdkWarning (HdkWarnExtraComment (L l _)) =
+ addWarning Opt_WarnInvalidHaddock l $
+ text "Multiple Haddock comments for a single entity are not allowed." $$
+ text "The extraneous comment will be ignored."
+
+collectHdkWarnings :: HdkSt -> [HdkWarn]
+collectHdkWarnings HdkSt{ hdk_st_pending, hdk_st_warnings } =
+ map HdkWarnInvalidComment hdk_st_pending -- leftover Haddock comments not inserted into the AST
+ ++ hdk_st_warnings
+
+{- *********************************************************************
+* *
+* addHaddock: a family of functions that processes the AST *
+* in concrete syntax order, adding documentation comments to it *
+* *
+********************************************************************* -}
+
+-- HasHaddock is a convenience class for overloading the addHaddock operation.
+-- Alternatively, we could define a family of monomorphic functions:
+--
+-- addHaddockSomeTypeX :: SomeTypeX -> HdkA SomeTypeX
+-- addHaddockAnotherTypeY :: AnotherTypeY -> HdkA AnotherTypeY
+-- addHaddockOneMoreTypeZ :: OneMoreTypeZ -> HdkA OneMoreTypeZ
+--
+-- But having a single name for all of them is just easier to read, and makes it clear
+-- that they all are of the form t -> HdkA t for some t.
+--
+-- If you need to handle a more complicated scenario that doesn't fit this
+-- pattern, it's always possible to define separate functions outside of this
+-- class, as is done in case of e.g. addHaddockConDeclField.
+--
+-- See Note [Adding Haddock comments to the syntax tree].
+class HasHaddock a where
+ addHaddock :: a -> HdkA a
+
+instance HasHaddock a => HasHaddock [a] where
+ addHaddock = traverse addHaddock
+
+-- -- | Module header comment
+-- module M (
+-- -- * Export list comment
+-- Item1,
+-- Item2,
+-- -- * Export list comment
+-- item3,
+-- item4
+-- ) where
+--
+instance HasHaddock (Located HsModule) where
+ addHaddock (L l_mod mod) = do
+ -- Step 1, get the module header documentation comment:
+ --
+ -- -- | Module header comment
+ -- module M where
+ --
+ -- Only do this when the module header exists.
+ headerDocs <-
+ for @Maybe (hsmodName mod) $ \(L l_name _) ->
+ extendHdkA l_name $ liftHdkA $ do
+ -- todo: register keyword location of 'module', see Note [Register keyword location]
+ docs <-
+ inLocRange (locRangeTo (getBufPos (srcSpanStart l_name))) $
+ takeHdkComments mkDocNext
+ selectDocString docs
+
+ -- Step 2, process documentation comments in the export list:
+ --
+ -- module M (
+ -- -- * Export list comment
+ -- Item1,
+ -- Item2,
+ -- -- * Export list comment
+ -- item3,
+ -- item4
+ -- ) where
+ --
+ -- Only do this when the export list exists.
+ hsmodExports' <- traverse @Maybe addHaddock (hsmodExports mod)
+
+ -- Step 3, register the import section to reject invalid comments:
+ --
+ -- import Data.Maybe
+ -- -- | rejected comment (cannot appear here)
+ -- import Data.Bool
+ --
+ traverse_ registerHdkA (hsmodImports mod)
+
+ -- Step 4, process declarations:
+ --
+ -- module M where
+ -- -- | Comment on D
+ -- data D = MkD -- ^ Comment on MkD
+ -- data C = MkC -- ^ Comment on MkC
+ -- -- ^ Comment on C
+ --
+ let layout_info = hsmodLayout mod
+ hsmodDecls' <- addHaddockInterleaveItems layout_info (mkDocHsDecl layout_info) (hsmodDecls mod)
+
+ pure $ L l_mod $
+ mod { hsmodExports = hsmodExports'
+ , hsmodDecls = hsmodDecls'
+ , hsmodHaddockModHeader = join @Maybe headerDocs }
+
+-- Only for module exports, not module imports.
+--
+-- module M (a, b, c) where -- use on this [LIE GhcPs]
+-- import I (a, b, c) -- do not use here!
+--
+-- Imports cannot have documentation comments anyway.
+instance HasHaddock (Located [LIE GhcPs]) where
+ addHaddock (L l_exports exports) =
+ extendHdkA l_exports $ do
+ exports' <- addHaddockInterleaveItems NoLayoutInfo mkDocIE exports
+ registerLocHdkA (srcLocSpan (srcSpanEnd l_exports)) -- Do not consume comments after the closing parenthesis
+ pure $ L l_exports exports'
+
+-- Needed to use 'addHaddockInterleaveItems' in 'instance HasHaddock (Located [LIE GhcPs])'.
+instance HasHaddock (LIE GhcPs) where
+ addHaddock a = a <$ registerHdkA a
+
+{- Add Haddock items to a list of non-Haddock items.
+Used to process export lists (with mkDocIE) and declarations (with mkDocHsDecl).
+
+For example:
+
+ module M where
+ -- | Comment on D
+ data D = MkD -- ^ Comment on MkD
+ data C = MkC -- ^ Comment on MkC
+ -- ^ Comment on C
+
+In this case, we should produce four HsDecl items (pseudo-code):
+
+ 1. DocD (DocCommentNext "Comment on D")
+ 2. TyClD (DataDecl "D" ... [ConDeclH98 "MkD" ... (Just "Comment on MkD")])
+ 3. TyClD (DataDecl "C" ... [ConDeclH98 "MkC" ... (Just "Comment on MkC")])
+ 4. DocD (DocCommentPrev "Comment on C")
+
+The inputs to addHaddockInterleaveItems are:
+
+ * layout_info :: LayoutInfo
+
+ In the example above, note that the indentation level inside the module is
+ 2 spaces. It would be represented as layout_info = VirtualBraces 2.
+
+ It is used to delimit the search space for comments when processing
+ declarations. Here, we restrict indentation levels to >=(2+1), so that when
+ we look up comment on MkC, we get "Comment on MkC" but not "Comment on C".
+
+ * get_doc_item :: PsLocated HdkComment -> Maybe a
+
+ This is the function used to look up documentation comments.
+ In the above example, get_doc_item = mkDocHsDecl layout_info,
+ and it will produce the following parts of the output:
+
+ DocD (DocCommentNext "Comment on D")
+ DocD (DocCommentPrev "Comment on C")
+
+ * The list of items. These are the declarations that will be annotated with
+ documentation comments.
+
+ Before processing:
+ TyClD (DataDecl "D" ... [ConDeclH98 "MkD" ... Nothing])
+ TyClD (DataDecl "C" ... [ConDeclH98 "MkC" ... Nothing])
+
+ After processing:
+ TyClD (DataDecl "D" ... [ConDeclH98 "MkD" ... (Just "Comment on MkD")])
+ TyClD (DataDecl "C" ... [ConDeclH98 "MkC" ... (Just "Comment on MkC")])
+-}
+addHaddockInterleaveItems
+ :: forall a.
+ HasHaddock a
+ => LayoutInfo
+ -> (PsLocated HdkComment -> Maybe a) -- Get a documentation item
+ -> [a] -- Unprocessed (non-documentation) items
+ -> HdkA [a] -- Documentation items & processed non-documentation items
+addHaddockInterleaveItems layout_info get_doc_item = go
+ where
+ go :: [a] -> HdkA [a]
+ go [] = liftHdkA (takeHdkComments get_doc_item)
+ go (item : items) = do
+ docItems <- liftHdkA (takeHdkComments get_doc_item)
+ item' <- with_layout_info (addHaddock item)
+ other_items <- go items
+ pure $ docItems ++ item':other_items
+
+ with_layout_info :: HdkA a -> HdkA a
+ with_layout_info = case layout_info of
+ NoLayoutInfo -> id
+ ExplicitBraces -> id
+ VirtualBraces n ->
+ let loc_range = mempty { loc_range_col = ColumnFrom (n+1) }
+ in hoistHdkA (inLocRange loc_range)
+
+instance HasHaddock (LHsDecl GhcPs) where
+ addHaddock ldecl =
+ extendHdkA (getLoc ldecl) $
+ traverse @Located addHaddock ldecl
+
+-- Process documentation comments *inside* a declaration, for example:
+--
+-- data T = MkT -- ^ Comment on MkT (inside DataDecl)
+-- f, g
+-- :: Int -- ^ Comment on Int (inside TypeSig)
+-- -> Bool -- ^ Comment on Bool (inside TypeSig)
+--
+-- Comments that relate to the entire declaration are processed elsewhere:
+--
+-- -- | Comment on T (not processed in this instance)
+-- data T = MkT
+--
+-- -- | Comment on f, g (not processed in this instance)
+-- f, g :: Int -> Bool
+-- f = ...
+-- g = ...
+--
+-- Such comments are inserted into the syntax tree as DocD declarations
+-- by addHaddockInterleaveItems, and then associated with other declarations
+-- in GHC.HsToCore.Docs (see DeclDocMap).
+--
+-- In this instance, we only process comments that relate to parts of the
+-- declaration, not to the declaration itself.
+instance HasHaddock (HsDecl GhcPs) where
+
+ -- Type signatures:
+ --
+ -- f, g
+ -- :: Int -- ^ Comment on Int
+ -- -> Bool -- ^ Comment on Bool
+ --
+ addHaddock (SigD _ (TypeSig _ names t)) = do
+ traverse_ registerHdkA names
+ t' <- addHaddock t
+ pure (SigD noExtField (TypeSig noExtField names t'))
+
+ -- Pattern synonym type signatures:
+ --
+ -- pattern MyPat
+ -- :: Bool -- ^ Comment on Bool
+ -- -> Maybe Bool -- ^ Comment on Maybe Bool
+ --
+ addHaddock (SigD _ (PatSynSig _ names t)) = do
+ traverse_ registerHdkA names
+ t' <- addHaddock t
+ pure (SigD noExtField (PatSynSig noExtField names t'))
+
+ -- Class method signatures and default signatures:
+ --
+ -- class C x where
+ -- method_of_c
+ -- :: Maybe x -- ^ Comment on Maybe x
+ -- -> IO () -- ^ Comment on IO ()
+ -- default method_of_c
+ -- :: Eq x
+ -- => Maybe x -- ^ Comment on Maybe x
+ -- -> IO () -- ^ Comment on IO ()
+ --
+ addHaddock (SigD _ (ClassOpSig _ is_dflt names t)) = do
+ traverse_ registerHdkA names
+ t' <- addHaddock t
+ pure (SigD noExtField (ClassOpSig noExtField is_dflt names t'))
+
+ -- Data/newtype declarations:
+ --
+ -- data T = MkT -- ^ Comment on MkT
+ -- A -- ^ Comment on A
+ -- B -- ^ Comment on B
+ --
+ -- data G where
+ -- -- | Comment on MkG
+ -- MkG :: A -- ^ Comment on A
+ -- -> B -- ^ Comment on B
+ -- -> G
+ --
+ -- newtype N = MkN { getN :: Natural } -- ^ Comment on N
+ -- deriving newtype (Eq {- ^ Comment on Eq N -})
+ -- deriving newtype (Ord {- ^ Comment on Ord N -})
+ --
+ addHaddock (TyClD _ decl)
+ | DataDecl { tcdLName, tcdTyVars, tcdFixity, tcdDataDefn = defn } <- decl
+ = do
+ registerHdkA tcdLName
+ defn' <- addHaddock defn
+ pure $
+ TyClD noExtField (DataDecl {
+ tcdDExt = noExtField,
+ tcdLName, tcdTyVars, tcdFixity,
+ tcdDataDefn = defn' })
+
+ -- Class declarations:
+ --
+ -- class C a where
+ -- -- | Comment on the first method
+ -- first_method :: a -> Bool
+ -- second_method :: a -> String
+ -- -- ^ Comment on the second method
+ --
+ addHaddock (TyClD _ decl)
+ | ClassDecl { tcdCExt = tcdLayout,
+ tcdCtxt, tcdLName, tcdTyVars, tcdFixity, tcdFDs,
+ tcdSigs, tcdMeths, tcdATs, tcdATDefs } <- decl
+ = do
+ registerHdkA tcdLName
+ -- todo: register keyword location of 'where', see Note [Register keyword location]
+ where_cls' <-
+ addHaddockInterleaveItems tcdLayout (mkDocHsDecl tcdLayout) $
+ flattenBindsAndSigs (tcdMeths, tcdSigs, tcdATs, tcdATDefs, [], [])
+ pure $
+ let (tcdMeths', tcdSigs', tcdATs', tcdATDefs', _, tcdDocs) = partitionBindsAndSigs where_cls'
+ decl' = ClassDecl { tcdCExt = tcdLayout
+ , tcdCtxt, tcdLName, tcdTyVars, tcdFixity, tcdFDs
+ , tcdSigs = tcdSigs'
+ , tcdMeths = tcdMeths'
+ , tcdATs = tcdATs'
+ , tcdATDefs = tcdATDefs'
+ , tcdDocs }
+ in TyClD noExtField decl'
+
+ -- Data family instances:
+ --
+ -- data instance D Bool where ... (same as data/newtype declarations)
+ -- data instance D Bool = ... (same as data/newtype declarations)
+ --
+ addHaddock (InstD _ decl)
+ | DataFamInstD { dfid_inst } <- decl
+ , DataFamInstDecl { dfid_eqn } <- dfid_inst
+ = do
+ dfid_eqn' <- case dfid_eqn of
+ HsIB _ (FamEqn { feqn_tycon, feqn_bndrs, feqn_pats, feqn_fixity, feqn_rhs })
+ -> do
+ registerHdkA feqn_tycon
+ feqn_rhs' <- addHaddock feqn_rhs
+ pure $
+ HsIB noExtField (FamEqn {
+ feqn_ext = noExtField,
+ feqn_tycon, feqn_bndrs, feqn_pats, feqn_fixity,
+ feqn_rhs = feqn_rhs' })
+ pure $ InstD noExtField (DataFamInstD {
+ dfid_ext = noExtField,
+ dfid_inst = DataFamInstDecl { dfid_eqn = dfid_eqn' } })
+
+ -- Type synonyms:
+ --
+ -- type T = Int -- ^ Comment on Int
+ --
+ addHaddock (TyClD _ decl)
+ | SynDecl { tcdLName, tcdTyVars, tcdFixity, tcdRhs } <- decl
+ = do
+ registerHdkA tcdLName
+ -- todo: register keyword location of '=', see Note [Register keyword location]
+ tcdRhs' <- addHaddock tcdRhs
+ pure $
+ TyClD noExtField (SynDecl {
+ tcdSExt = noExtField,
+ tcdLName, tcdTyVars, tcdFixity,
+ tcdRhs = tcdRhs' })
+
+ -- Foreign imports:
+ --
+ -- foreign import ccall unsafe
+ -- o :: Float -- ^ The input float
+ -- -> IO Float -- ^ The output float
+ --
+ addHaddock (ForD _ decl) = do
+ registerHdkA (fd_name decl)
+ fd_sig_ty' <- addHaddock (fd_sig_ty decl)
+ pure $ ForD noExtField (decl{ fd_sig_ty = fd_sig_ty' })
+
+ -- Other declarations
+ addHaddock d = pure d
+
+-- The right-hand side of a data/newtype declaration or data family instance.
+instance HasHaddock (HsDataDefn GhcPs) where
+ addHaddock defn@HsDataDefn{} = do
+
+ -- Register the kind signature:
+ -- data D :: Type -> Type where ...
+ -- data instance D Bool :: Type where ...
+ traverse_ @Maybe registerHdkA (dd_kindSig defn)
+ -- todo: register keyword location of '=' or 'where', see Note [Register keyword location]
+
+ -- Process the data constructors:
+ --
+ -- data T
+ -- = MkT1 Int Bool -- ^ Comment on MkT1
+ -- | MkT2 Char Int -- ^ Comment on MkT2
+ --
+ dd_cons' <- addHaddock (dd_cons defn)
+
+ -- Process the deriving clauses:
+ --
+ -- newtype N = MkN Natural
+ -- deriving (Eq {- ^ Comment on Eq N -})
+ -- deriving (Ord {- ^ Comment on Ord N -})
+ --
+ dd_derivs' <- addHaddock (dd_derivs defn)
+
+ pure $ defn { dd_cons = dd_cons',
+ dd_derivs = dd_derivs' }
+
+-- Process the deriving clauses of a data/newtype declaration.
+-- Not used for standalone deriving.
+instance HasHaddock (HsDeriving GhcPs) where
+ addHaddock lderivs =
+ extendHdkA (getLoc lderivs) $
+ traverse @Located addHaddock lderivs
+
+-- Process a single deriving clause of a data/newtype declaration:
+--
+-- newtype N = MkN Natural
+-- deriving newtype (Eq {- ^ Comment on Eq N -})
+-- deriving (Ord {- ^ Comment on Ord N -}) via Down N
+--
+-- Not used for standalone deriving.
+instance HasHaddock (LHsDerivingClause GhcPs) where
+ addHaddock lderiv =
+ extendHdkA (getLoc lderiv) $
+ for @Located lderiv $ \deriv ->
+ case deriv of
+ HsDerivingClause { deriv_clause_strategy, deriv_clause_tys } -> do
+ let
+ -- 'stock', 'anyclass', and 'newtype' strategies come
+ -- before the clause types.
+ --
+ -- 'via' comes after.
+ --
+ -- See tests/.../T11768.hs
+ (register_strategy_before, register_strategy_after) =
+ case deriv_clause_strategy of
+ Nothing -> (pure (), pure ())
+ Just (L l (ViaStrategy _)) -> (pure (), registerLocHdkA l)
+ Just (L l _) -> (registerLocHdkA l, pure ())
+ register_strategy_before
+ deriv_clause_tys' <-
+ extendHdkA (getLoc deriv_clause_tys) $
+ traverse @Located addHaddock deriv_clause_tys
+ register_strategy_after
+ pure HsDerivingClause
+ { deriv_clause_ext = noExtField,
+ deriv_clause_strategy,
+ deriv_clause_tys = deriv_clause_tys' }
+
+-- Process a single data constructor declaration, which may come in one of the
+-- following forms:
+--
+-- 1. H98-syntax PrefixCon:
+-- data T =
+-- MkT -- ^ Comment on MkT
+-- Int -- ^ Comment on Int
+-- Bool -- ^ Comment on Bool
+--
+-- 2. H98-syntax InfixCon:
+-- data T =
+-- Int -- ^ Comment on Int
+-- :+ -- ^ Comment on (:+)
+-- Bool -- ^ Comment on Bool
+--
+-- 3. H98-syntax RecCon:
+-- data T =
+-- MkT { int_field :: Int, -- ^ Comment on int_field
+-- bool_field :: Bool } -- ^ Comment on bool_field
+--
+-- 4. GADT-syntax PrefixCon:
+-- data T where
+-- -- | Comment on MkT
+-- MkT :: Int -- ^ Comment on Int
+-- -> Bool -- ^ Comment on Bool
+-- -> T
+--
+-- 5. GADT-syntax RecCon:
+-- data T where
+-- -- | Comment on MkT
+-- MkT :: { int_field :: Int, -- ^ Comment on int_field
+-- bool_field :: Bool } -- ^ Comment on bool_field
+-- -> T
+--
+instance HasHaddock (LConDecl GhcPs) where
+ addHaddock (L l_con_decl con_decl) =
+ extendHdkA l_con_decl $
+ case con_decl of
+ ConDeclGADT { con_g_ext, con_names, con_forall, con_qvars, con_mb_cxt, con_args, con_res_ty } -> do
+ -- discardHasInnerDocs is ok because we don't need this info for GADTs.
+ con_doc' <- discardHasInnerDocs $ getConDoc (getLoc (head con_names))
+ con_args' <-
+ case con_args of
+ PrefixCon ts -> PrefixCon <$> addHaddock ts
+ RecCon (L l_rec flds) -> do
+ -- discardHasInnerDocs is ok because we don't need this info for GADTs.
+ flds' <- traverse (discardHasInnerDocs . addHaddockConDeclField) flds
+ pure $ RecCon (L l_rec flds')
+ InfixCon _ _ -> panic "ConDeclGADT InfixCon"
+ con_res_ty' <- addHaddock con_res_ty
+ pure $ L l_con_decl $
+ ConDeclGADT { con_g_ext, con_names, con_forall, con_qvars, con_mb_cxt,
+ con_doc = con_doc',
+ con_args = con_args',
+ con_res_ty = con_res_ty' }
+ ConDeclH98 { con_ext, con_name, con_forall, con_ex_tvs, con_mb_cxt, con_args } ->
+ addConTrailingDoc (srcSpanEnd l_con_decl) $
+ case con_args of
+ PrefixCon ts -> do
+ con_doc' <- getConDoc (getLoc con_name)
+ ts' <- traverse addHaddockConDeclFieldTy ts
+ pure $ L l_con_decl $
+ ConDeclH98 { con_ext, con_name, con_forall, con_ex_tvs, con_mb_cxt,
+ con_doc = con_doc',
+ con_args = PrefixCon ts' }
+ InfixCon t1 t2 -> do
+ t1' <- addHaddockConDeclFieldTy t1
+ con_doc' <- getConDoc (getLoc con_name)
+ t2' <- addHaddockConDeclFieldTy t2
+ pure $ L l_con_decl $
+ ConDeclH98 { con_ext, con_name, con_forall, con_ex_tvs, con_mb_cxt,
+ con_doc = con_doc',
+ con_args = InfixCon t1' t2' }
+ RecCon (L l_rec flds) -> do
+ con_doc' <- getConDoc (getLoc con_name)
+ flds' <- traverse addHaddockConDeclField flds
+ pure $ L l_con_decl $
+ ConDeclH98 { con_ext, con_name, con_forall, con_ex_tvs, con_mb_cxt,
+ con_doc = con_doc',
+ con_args = RecCon (L l_rec flds') }
+ XConDecl (ConDeclGADTPrefixPs { con_gp_names, con_gp_ty }) -> do
+ -- discardHasInnerDocs is ok because we don't need this info for GADTs.
+ con_gp_doc' <- discardHasInnerDocs $ getConDoc (getLoc (head con_gp_names))
+ con_gp_ty' <- addHaddock con_gp_ty
+ pure $ L l_con_decl $
+ XConDecl (ConDeclGADTPrefixPs
+ { con_gp_names,
+ con_gp_ty = con_gp_ty',
+ con_gp_doc = con_gp_doc' })
+
+-- Keep track of documentation comments on the data constructor or any of its
+-- fields.
+--
+-- See Note [Trailing comment on constructor declaration]
+type ConHdkA = WriterT HasInnerDocs HdkA
+
+-- Does the data constructor declaration have any inner (non-trailing)
+-- documentation comments?
+--
+-- Example when HasInnerDocs is True:
+--
+-- data X =
+-- MkX -- ^ inner comment
+-- Field1 -- ^ inner comment
+-- Field2 -- ^ inner comment
+-- Field3 -- ^ trailing comment
+--
+-- Example when HasInnerDocs is False:
+--
+-- data Y = MkY Field1 Field2 Field3 -- ^ trailing comment
+--
+-- See Note [Trailing comment on constructor declaration]
+newtype HasInnerDocs = HasInnerDocs Bool
+ deriving (Semigroup, Monoid) via Data.Monoid.Any
+
+-- Run ConHdkA by discarding the HasInnerDocs info when we have no use for it.
+--
+-- We only do this when processing data declarations that use GADT syntax,
+-- because only the H98 syntax declarations have special treatment for the
+-- trailing documentation comment.
+--
+-- See Note [Trailing comment on constructor declaration]
+discardHasInnerDocs :: ConHdkA a -> HdkA a
+discardHasInnerDocs = fmap fst . runWriterT
+
+-- Get the documentation comment associated with the data constructor in a
+-- data/newtype declaration.
+getConDoc
+ :: SrcSpan -- Location of the data constructor
+ -> ConHdkA (Maybe LHsDocString)
+getConDoc l =
+ WriterT $ extendHdkA l $ liftHdkA $ do
+ mDoc <- getPrevNextDoc l
+ return (mDoc, HasInnerDocs (isJust mDoc))
+
+-- Add documentation comment to a data constructor field.
+-- Used for PrefixCon and InfixCon.
+addHaddockConDeclFieldTy
+ :: HsScaled GhcPs (LHsType GhcPs)
+ -> ConHdkA (HsScaled GhcPs (LHsType GhcPs))
+addHaddockConDeclFieldTy (HsScaled mult (L l t)) =
+ WriterT $ extendHdkA l $ liftHdkA $ do
+ mDoc <- getPrevNextDoc l
+ return (HsScaled mult (mkLHsDocTy (L l t) mDoc),
+ HasInnerDocs (isJust mDoc))
+
+-- Add documentation comment to a data constructor field.
+-- Used for RecCon.
+addHaddockConDeclField
+ :: LConDeclField GhcPs
+ -> ConHdkA (LConDeclField GhcPs)
+addHaddockConDeclField (L l_fld fld) =
+ WriterT $ extendHdkA l_fld $ liftHdkA $ do
+ cd_fld_doc <- getPrevNextDoc l_fld
+ return (L l_fld (fld { cd_fld_doc }),
+ HasInnerDocs (isJust cd_fld_doc))
+
+-- 1. Process a H98-syntax data constructor declaration in a context with no
+-- access to the trailing documentation comment (by running the provided
+-- ConHdkA computation).
+--
+-- 2. Then grab the trailing comment (if it exists) and attach it where
+-- appropriate: either to the data constructor itself or to its last field,
+-- depending on HasInnerDocs.
+--
+-- See Note [Trailing comment on constructor declaration]
+addConTrailingDoc
+ :: SrcLoc -- The end of a data constructor declaration.
+ -- Any docprev comment past this point is considered trailing.
+ -> ConHdkA (LConDecl GhcPs)
+ -> HdkA (LConDecl GhcPs)
+addConTrailingDoc l_sep =
+ hoistHdkA add_trailing_doc . runWriterT
+ where
+ add_trailing_doc
+ :: HdkM (LConDecl GhcPs, HasInnerDocs)
+ -> HdkM (LConDecl GhcPs)
+ add_trailing_doc m = do
+ (L l con_decl, HasInnerDocs has_inner_docs) <-
+ inLocRange (locRangeTo (getBufPos l_sep)) m
+ -- inLocRange delimits the context so that the inner computation
+ -- will not consume the trailing documentation comment.
+ case con_decl of
+ ConDeclH98{} -> do
+ trailingDocs <-
+ inLocRange (locRangeFrom (getBufPos l_sep)) $
+ takeHdkComments mkDocPrev
+ if null trailingDocs
+ then return (L l con_decl)
+ else do
+ if has_inner_docs then do
+ let mk_doc_ty :: HsScaled GhcPs (LHsType GhcPs)
+ -> HdkM (HsScaled GhcPs (LHsType GhcPs))
+ mk_doc_ty x@(HsScaled _ (L _ HsDocTy{})) =
+ -- Happens in the following case:
+ --
+ -- data T =
+ -- MkT
+ -- -- | Comment on SomeField
+ -- SomeField
+ -- -- ^ Another comment on SomeField? (rejected)
+ --
+ -- See tests/.../haddockExtraDocs.hs
+ x <$ reportExtraDocs trailingDocs
+ mk_doc_ty (HsScaled mult (L l' t)) = do
+ doc <- selectDocString trailingDocs
+ return $ HsScaled mult (mkLHsDocTy (L l' t) doc)
+ let mk_doc_fld :: LConDeclField GhcPs
+ -> HdkM (LConDeclField GhcPs)
+ mk_doc_fld x@(L _ (ConDeclField { cd_fld_doc = Just _ })) =
+ -- Happens in the following case:
+ --
+ -- data T =
+ -- MkT {
+ -- -- | Comment on SomeField
+ -- someField :: SomeField
+ -- } -- ^ Another comment on SomeField? (rejected)
+ --
+ -- See tests/.../haddockExtraDocs.hs
+ x <$ reportExtraDocs trailingDocs
+ mk_doc_fld (L l' con_fld) = do
+ doc <- selectDocString trailingDocs
+ return $ L l' (con_fld { cd_fld_doc = doc })
+ con_args' <- case con_args con_decl of
+ x@(PrefixCon []) -> x <$ reportExtraDocs trailingDocs
+ x@(RecCon (L _ [])) -> x <$ reportExtraDocs trailingDocs
+ PrefixCon ts -> PrefixCon <$> mapLastM mk_doc_ty ts
+ InfixCon t1 t2 -> InfixCon t1 <$> mk_doc_ty t2
+ RecCon (L l_rec flds) -> do
+ flds' <- mapLastM mk_doc_fld flds
+ return (RecCon (L l_rec flds'))
+ return $ L l (con_decl{ con_args = con_args' })
+ else do
+ con_doc' <- selectDocString (con_doc con_decl `mcons` trailingDocs)
+ return $ L l (con_decl{ con_doc = con_doc' })
+ _ -> panic "addConTrailingDoc: non-H98 ConDecl"
+
+{- Note [Trailing comment on constructor declaration]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The trailing comment after a constructor declaration is associated with the
+constructor itself when there are no other comments inside the declaration:
+
+ data T = MkT A B -- ^ Comment on MkT
+ data T = MkT { x :: A } -- ^ Comment on MkT
+
+When there are other comments, the trailing comment applies to the last field:
+
+ data T = MkT -- ^ Comment on MkT
+ A -- ^ Comment on A
+ B -- ^ Comment on B
+
+ data T =
+ MkT { a :: A -- ^ Comment on a
+ , b :: B -- ^ Comment on b
+ , c :: C } -- ^ Comment on c
+
+This makes the trailing comment context-sensitive. Example:
+ data T =
+ -- | comment 1
+ MkT Int Bool -- ^ comment 2
+
+ Here, "comment 2" applies to the Bool field.
+ But if we removed "comment 1", then "comment 2" would be apply to the data
+ constructor rather than its field.
+
+All of this applies to H98-style data declarations only.
+GADTSyntax data constructors don't have any special treatment for the trailing comment.
+
+We implement this in two steps:
+
+ 1. Process the data constructor declaration in a delimited context where the
+ trailing documentation comment is not visible. Delimiting the context is done
+ in addConTrailingDoc.
+
+ When processing the declaration, track whether the constructor or any of
+ its fields have a documentation comment associated with them.
+ This is done using WriterT HasInnerDocs, see ConHdkA.
+
+ 2. Depending on whether HasInnerDocs is True or False, attach the
+ trailing documentation comment to the data constructor itself
+ or to its last field.
+-}
+
+instance HasHaddock a => HasHaddock (HsScaled GhcPs a) where
+ addHaddock (HsScaled mult a) = HsScaled mult <$> addHaddock a
+
+instance HasHaddock (LHsSigWcType GhcPs) where
+ addHaddock (HsWC _ t) = HsWC noExtField <$> addHaddock t
+
+instance HasHaddock (LHsSigType GhcPs) where
+ addHaddock (HsIB _ t) = HsIB noExtField <$> addHaddock t
+
+-- Process a type, adding documentation comments to function arguments
+-- and the result. Many formatting styles are supported.
+--
+-- my_function ::
+-- forall a.
+-- Eq a =>
+-- Maybe a -> -- ^ Comment on Maybe a (function argument)
+-- Bool -> -- ^ Comment on Bool (function argument)
+-- String -- ^ Comment on String (the result)
+--
+-- my_function
+-- :: forall a. Eq a
+-- => Maybe a -- ^ Comment on Maybe a (function argument)
+-- -> Bool -- ^ Comment on Bool (function argument)
+-- -> String -- ^ Comment on String (the result)
+--
+-- my_function ::
+-- forall a. Eq a =>
+-- -- | Comment on Maybe a (function argument)
+-- Maybe a ->
+-- -- | Comment on Bool (function argument)
+-- Bool ->
+-- -- | Comment on String (the result)
+-- String
+--
+-- This is achieved by simply ignoring (not registering the location of) the
+-- function arrow (->).
+instance HasHaddock (LHsType GhcPs) where
+ addHaddock (L l t) =
+ extendHdkA l $
+ case t of
+
+ -- forall a b c. t
+ HsForAllTy _ tele body -> do
+ registerLocHdkA (getForAllTeleLoc tele)
+ body' <- addHaddock body
+ pure $ L l (HsForAllTy noExtField tele body')
+
+ -- (Eq a, Num a) => t
+ HsQualTy _ lhs rhs -> do
+ registerHdkA lhs
+ rhs' <- addHaddock rhs
+ pure $ L l (HsQualTy noExtField lhs rhs')
+
+ -- arg -> res
+ HsFunTy _ mult lhs rhs -> do
+ lhs' <- addHaddock lhs
+ rhs' <- addHaddock rhs
+ pure $ L l (HsFunTy noExtField mult lhs' rhs')
+
+ -- other types
+ _ -> liftHdkA $ do
+ mDoc <- getPrevNextDoc l
+ return (mkLHsDocTy (L l t) mDoc)
+
+{- *********************************************************************
+* *
+* HdkA: a layer over HdkM that propagates location information *
+* *
+********************************************************************* -}
+
+-- See Note [Adding Haddock comments to the syntax tree].
+--
+-- 'HdkA' provides a way to propagate location information from surrounding
+-- computations:
+--
+-- left_neighbour <*> HdkA inner_span inner_m <*> right_neighbour
+--
+-- Here, the following holds:
+--
+-- * the 'left_neighbour' will only see Haddock comments until 'bufSpanStart' of 'inner_span'
+-- * the 'right_neighbour' will only see Haddock comments after 'bufSpanEnd' of 'inner_span'
+-- * the 'inner_m' will only see Haddock comments between its 'left_neighbour' and its 'right_neighbour'
+--
+-- In other words, every computation:
+--
+-- * delimits the surrounding computations
+-- * is delimited by the surrounding computations
+--
+-- Therefore, a 'HdkA' computation must be always considered in the context in
+-- which it is used.
+data HdkA a =
+ HdkA
+ !(Maybe BufSpan) -- Just b <=> BufSpan occupied by the processed AST element.
+ -- The surrounding computations will not look inside.
+ --
+ -- Nothing <=> No BufSpan (e.g. when the HdkA is constructed by 'pure' or 'liftHdkA').
+ -- The surrounding computations are not delimited.
+
+ !(HdkM a) -- The stateful computation that looks up Haddock comments and
+ -- adds them to the resulting AST node.
+
+ deriving (Functor)
+
+instance Applicative HdkA where
+ HdkA l1 m1 <*> HdkA l2 m2 =
+ HdkA
+ (l1 <> l2) -- The combined BufSpan that covers both subcomputations.
+ --
+ -- The Semigroup instance for Maybe quite conveniently does the right thing:
+ -- Nothing <> b = b
+ -- a <> Nothing = a
+ -- Just a <> Just b = Just (a <> b)
+
+ (delim1 m1 <*> delim2 m2) -- Stateful computations are run in left-to-right order,
+ -- without any smart reordering strategy. So users of this
+ -- operation must take care to traverse the AST
+ -- in concrete syntax order.
+ -- See Note [Smart reordering in HdkA (or lack of thereof)]
+ --
+ -- Each computation is delimited ("sandboxed")
+ -- in a way that it doesn't see any Haddock
+ -- comments past the neighbouring AST node.
+ -- These delim1/delim2 are key to how HdkA operates.
+ where
+ -- Delimit the LHS by the location information from the RHS
+ delim1 = inLocRange (locRangeTo (fmap @Maybe bufSpanStart l2))
+ -- Delimit the RHS by the location information from the LHS
+ delim2 = inLocRange (locRangeFrom (fmap @Maybe bufSpanEnd l1))
+
+ pure a =
+ -- Return a value without performing any stateful computation, and without
+ -- any delimiting effect on the surrounding computations.
+ liftHdkA (pure a)
+
+{- Note [Smart reordering in HdkA (or lack of thereof)]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When traversing the AST, the user must take care to traverse it in concrete
+syntax order.
+
+For example, when processing HsFunTy, it's important to get it right and write
+it like so:
+
+ HsFunTy _ mult lhs rhs -> do
+ lhs' <- addHaddock lhs
+ rhs' <- addHaddock rhs
+ pure $ L l (HsFunTy noExtField mult lhs' rhs')
+
+Rather than like so:
+
+ HsFunTy _ mult lhs rhs -> do
+ rhs' <- addHaddock rhs -- bad! wrong order
+ lhs' <- addHaddock lhs -- bad! wrong order
+ pure $ L l (HsFunTy noExtField mult lhs' rhs')
+
+This is somewhat bug-prone, so we could try to fix this with some Applicative
+magic. When we define (<*>) for HdkA, why not reorder the computations as
+necessary? In pseudo-code:
+
+ a1 <*> a2 | a1 `before` a2 = ... normal processing ...
+ | otherwise = a1 <**> a2
+
+While this trick could work for any two *adjacent* AST elements out of order
+(as in HsFunTy example above), it would fail in more elaborate scenarios (e.g.
+processing a list of declarations out of order).
+
+If it's not obvious why this trick doesn't work, ponder this: it's a bit like trying to get
+a sorted list by defining a 'smart' concatenation operator in the following manner:
+
+ a ?++ b | a <= b = a ++ b
+ | otherwise = b ++ a
+
+At first glance it seems to work:
+
+ ghci> [1] ?++ [2] ?++ [3]
+ [1,2,3]
+
+ ghci> [2] ?++ [1] ?++ [3]
+ [1,2,3] -- wow, sorted!
+
+But it actually doesn't:
+
+ ghci> [3] ?++ [1] ?++ [2]
+ [1,3,2] -- not sorted...
+-}
+
+-- Run a HdkA computation in an unrestricted LocRange. This is only used at the
+-- top level to run the final computation for the entire module.
+runHdkA :: HdkA a -> HdkSt -> (a, HdkSt)
+runHdkA (HdkA _ m) = unHdkM m mempty
+
+-- Let the neighbours know about an item at this location.
+--
+-- Consider this example:
+--
+-- class -- | peculiarly placed comment
+-- MyClass a where
+-- my_method :: a -> a
+--
+-- How do we know to reject the "peculiarly placed comment" instead of
+-- associating it with my_method? Its indentation level matches.
+--
+-- But clearly, there's "MyClass a where" separating the comment and my_method.
+-- To take it into account, we must register its location using registerLocHdkA
+-- or registerHdkA.
+--
+-- See Note [Register keyword location].
+-- See Note [Adding Haddock comments to the syntax tree].
+registerLocHdkA :: SrcSpan -> HdkA ()
+registerLocHdkA l = HdkA (getBufSpan l) (pure ())
+
+-- Let the neighbours know about an item at this location.
+-- A small wrapper over registerLocHdkA.
+--
+-- See Note [Adding Haddock comments to the syntax tree].
+registerHdkA :: Located a -> HdkA ()
+registerHdkA a = registerLocHdkA (getLoc a)
+
+-- Modify the action of a HdkA computation.
+hoistHdkA :: (HdkM a -> HdkM b) -> HdkA a -> HdkA b
+hoistHdkA f (HdkA l m) = HdkA l (f m)
+
+-- Lift a HdkM computation to HdkA.
+liftHdkA :: HdkM a -> HdkA a
+liftHdkA = HdkA mempty
+
+-- Extend the declared location span of a 'HdkA' computation:
+--
+-- left_neighbour <*> extendHdkA l x <*> right_neighbour
+--
+-- The declared location of 'x' now includes 'l', so that the surrounding
+-- computations 'left_neighbour' and 'right_neighbour' will not look for
+-- Haddock comments inside the 'l' location span.
+extendHdkA :: SrcSpan -> HdkA a -> HdkA a
+extendHdkA l' (HdkA l m) = HdkA (getBufSpan l' <> l) m
+
+
+{- *********************************************************************
+* *
+* HdkM: a stateful computation to associate *
+* accumulated documentation comments with AST nodes *
+* *
+********************************************************************* -}
+
+-- The state of 'HdkM' contains a list of pending Haddock comments. We go
+-- over the AST, looking up these comments using 'takeHdkComments' and removing
+-- them from the state. The remaining, un-removed ones are ignored with a
+-- warning (-Winvalid-haddock). Also, using a state means we never use the same
+-- Haddock twice.
+--
+-- See Note [Adding Haddock comments to the syntax tree].
+newtype HdkM a = HdkM (ReaderT LocRange (State HdkSt) a)
+ deriving (Functor, Applicative, Monad)
+
+-- | The state of HdkM.
+data HdkSt =
+ HdkSt
+ { hdk_st_pending :: [PsLocated HdkComment]
+ -- a list of pending (unassociated with an AST node)
+ -- Haddock comments, sorted by location: in ascending order of the starting 'BufPos'
+ , hdk_st_warnings :: [HdkWarn]
+ -- accumulated warnings (order doesn't matter)
+ }
+
+-- | Warnings accumulated in HdkM.
+data HdkWarn
+ = HdkWarnInvalidComment (PsLocated HdkComment)
+ | HdkWarnExtraComment LHsDocString
+
+-- 'HdkM' without newtype wrapping/unwrapping.
+type InlineHdkM a = LocRange -> HdkSt -> (a, HdkSt)
+
+mkHdkM :: InlineHdkM a -> HdkM a
+unHdkM :: HdkM a -> InlineHdkM a
+mkHdkM = coerce
+unHdkM = coerce
+
+-- Restrict the range in which a HdkM computation will look up comments:
+--
+-- inLocRange r1 $
+-- inLocRange r2 $
+-- takeHdkComments ... -- Only takes comments in the (r1 <> r2) location range.
+--
+-- Note that it does not blindly override the range but tightens it using (<>).
+-- At many use sites, you will see something along the lines of:
+--
+-- inLocRange (locRangeTo end_pos) $ ...
+--
+-- And 'locRangeTo' defines a location range from the start of the file to
+-- 'end_pos'. This does not mean that we now search for every comment from the
+-- start of the file, as this restriction will be combined with other
+-- restrictions. Somewhere up the callstack we might have:
+--
+-- inLocRange (locRangeFrom start_pos) $ ...
+--
+-- The net result is that the location range is delimited by 'start_pos' on
+-- one side and by 'end_pos' on the other side.
+--
+-- In 'HdkA', every (<*>) may restrict the location range of its
+-- subcomputations.
+inLocRange :: LocRange -> HdkM a -> HdkM a
+inLocRange r (HdkM m) = HdkM (local (mappend r) m)
+
+-- Take the Haddock comments that satisfy the matching function,
+-- leaving the rest pending.
+takeHdkComments :: forall a. (PsLocated HdkComment -> Maybe a) -> HdkM [a]
+takeHdkComments f =
+ mkHdkM $
+ \(LocRange hdk_from hdk_to hdk_col) ->
+ \hdk_st ->
+ let
+ comments = hdk_st_pending hdk_st
+ (comments_before_range, comments') = break (is_after hdk_from) comments
+ (comments_in_range, comments_after_range) = span (is_before hdk_to <&&> is_indented hdk_col) comments'
+ (items, other_comments) = foldr add_comment ([], []) comments_in_range
+ remaining_comments = comments_before_range ++ other_comments ++ comments_after_range
+ hdk_st' = hdk_st{ hdk_st_pending = remaining_comments }
+ in
+ (items, hdk_st')
+ where
+ is_after StartOfFile _ = True
+ is_after (StartLoc l) (L l_comment _) = bufSpanStart (psBufSpan l_comment) >= l
+ is_before EndOfFile _ = True
+ is_before (EndLoc l) (L l_comment _) = bufSpanStart (psBufSpan l_comment) <= l
+ is_indented (ColumnFrom n) (L l_comment _) = srcSpanStartCol (psRealSpan l_comment) >= n
+
+ add_comment
+ :: PsLocated HdkComment
+ -> ([a], [PsLocated HdkComment])
+ -> ([a], [PsLocated HdkComment])
+ add_comment hdk_comment (items, other_hdk_comments) =
+ case f hdk_comment of
+ Just item -> (item : items, other_hdk_comments)
+ Nothing -> (items, hdk_comment : other_hdk_comments)
+
+-- Get the docnext or docprev comment for an AST node at the given source span.
+getPrevNextDoc :: SrcSpan -> HdkM (Maybe LHsDocString)
+getPrevNextDoc l = do
+ let (l_start, l_end) = (srcSpanStart l, srcSpanEnd l)
+ before_t = locRangeTo (getBufPos l_start)
+ after_t = locRangeFrom (getBufPos l_end)
+ nextDocs <- inLocRange before_t $ takeHdkComments mkDocNext
+ prevDocs <- inLocRange after_t $ takeHdkComments mkDocPrev
+ selectDocString (nextDocs ++ prevDocs)
+
+appendHdkWarning :: HdkWarn -> HdkM ()
+appendHdkWarning e = HdkM (ReaderT (\_ -> modify append_warn))
+ where
+ append_warn hdk_st = hdk_st { hdk_st_warnings = e : hdk_st_warnings hdk_st }
+
+selectDocString :: [LHsDocString] -> HdkM (Maybe LHsDocString)
+selectDocString = select . filterOut (isEmptyDocString . unLoc)
+ where
+ select [] = return Nothing
+ select [doc] = return (Just doc)
+ select (doc : extra_docs) = do
+ reportExtraDocs extra_docs
+ return (Just doc)
+
+reportExtraDocs :: [LHsDocString] -> HdkM ()
+reportExtraDocs =
+ traverse_ (\extra_doc -> appendHdkWarning (HdkWarnExtraComment extra_doc))
+
+{- *********************************************************************
+* *
+* Matching functions for extracting documentation comments *
+* *
+********************************************************************* -}
+
+mkDocHsDecl :: LayoutInfo -> PsLocated HdkComment -> Maybe (LHsDecl GhcPs)
+mkDocHsDecl layout_info a = mapLoc (DocD noExtField) <$> mkDocDecl layout_info a
+
+mkDocDecl :: LayoutInfo -> PsLocated HdkComment -> Maybe LDocDecl
+mkDocDecl layout_info (L l_comment hdk_comment)
+ | indent_mismatch = Nothing
+ | otherwise =
+ Just $ L (mkSrcSpanPs l_comment) $
+ case hdk_comment of
+ HdkCommentNext doc -> DocCommentNext doc
+ HdkCommentPrev doc -> DocCommentPrev doc
+ HdkCommentNamed s doc -> DocCommentNamed s doc
+ HdkCommentSection n doc -> DocGroup n doc
+ where
+ -- 'indent_mismatch' checks if the documentation comment has the exact
+ -- indentation level expected by the parent node.
+ --
+ -- For example, when extracting documentation comments between class
+ -- method declarations, there are three cases to consider:
+ --
+ -- 1. Indent matches (indent_mismatch=False):
+ -- class C a where
+ -- f :: a -> a
+ -- -- ^ doc on f
+ --
+ -- 2. Indented too much (indent_mismatch=True):
+ -- class C a where
+ -- f :: a -> a
+ -- -- ^ indent mismatch
+ --
+ -- 3. Indented too little (indent_mismatch=True):
+ -- class C a where
+ -- f :: a -> a
+ -- -- ^ indent mismatch
+ indent_mismatch = case layout_info of
+ NoLayoutInfo -> False
+ ExplicitBraces -> False
+ VirtualBraces n -> n /= srcSpanStartCol (psRealSpan l_comment)
+
+mkDocIE :: PsLocated HdkComment -> Maybe (LIE GhcPs)
+mkDocIE (L l_comment hdk_comment) =
+ case hdk_comment of
+ HdkCommentSection n doc -> Just $ L l (IEGroup noExtField n doc)
+ HdkCommentNamed s _doc -> Just $ L l (IEDocNamed noExtField s)
+ HdkCommentNext doc -> Just $ L l (IEDoc noExtField doc)
+ _ -> Nothing
+ where l = mkSrcSpanPs l_comment
+
+mkDocNext :: PsLocated HdkComment -> Maybe LHsDocString
+mkDocNext (L l (HdkCommentNext doc)) = Just $ L (mkSrcSpanPs l) doc
+mkDocNext _ = Nothing
+
+mkDocPrev :: PsLocated HdkComment -> Maybe LHsDocString
+mkDocPrev (L l (HdkCommentPrev doc)) = Just $ L (mkSrcSpanPs l) doc
+mkDocPrev _ = Nothing
+
+
+{- *********************************************************************
+* *
+* LocRange: a location range *
+* *
+********************************************************************* -}
+
+-- A location range for extracting documentation comments.
+data LocRange =
+ LocRange
+ { loc_range_from :: !LowerLocBound,
+ loc_range_to :: !UpperLocBound,
+ loc_range_col :: !ColumnBound }
+
+instance Semigroup LocRange where
+ LocRange from1 to1 col1 <> LocRange from2 to2 col2 =
+ LocRange (from1 <> from2) (to1 <> to2) (col1 <> col2)
+
+instance Monoid LocRange where
+ mempty = LocRange mempty mempty mempty
+
+-- The location range from the specified position to the end of the file.
+locRangeFrom :: Maybe BufPos -> LocRange
+locRangeFrom (Just l) = mempty { loc_range_from = StartLoc l }
+locRangeFrom Nothing = mempty
+
+-- The location range from the start of the file to the specified position.
+locRangeTo :: Maybe BufPos -> LocRange
+locRangeTo (Just l) = mempty { loc_range_to = EndLoc l }
+locRangeTo Nothing = mempty
+
+-- Represents a predicate on BufPos:
+--
+-- LowerLocBound | BufPos -> Bool
+-- --------------+-----------------
+-- StartOfFile | const True
+-- StartLoc p | (>= p)
+--
+-- The semigroup instance corresponds to (&&).
+--
+-- We don't use the BufPos -> Bool representation
+-- as it would lead to redundant checks.
+--
+-- That is, instead of
+--
+-- (pos >= 20) && (pos >= 30) && (pos >= 40)
+--
+-- We'd rather only do the (>=40) check. So we reify the predicate to make
+-- sure we only check for the most restrictive bound.
+data LowerLocBound = StartOfFile | StartLoc !BufPos
+
+instance Semigroup LowerLocBound where
+ StartOfFile <> l = l
+ l <> StartOfFile = l
+ StartLoc l1 <> StartLoc l2 = StartLoc (max l1 l2)
+
+instance Monoid LowerLocBound where
+ mempty = StartOfFile
+
+-- Represents a predicate on BufPos:
+--
+-- UpperLocBound | BufPos -> Bool
+-- --------------+-----------------
+-- EndOfFile | const True
+-- EndLoc p | (<= p)
+--
+-- The semigroup instance corresponds to (&&).
+--
+-- We don't use the BufPos -> Bool representation
+-- as it would lead to redundant checks.
+--
+-- That is, instead of
+--
+-- (pos <= 40) && (pos <= 30) && (pos <= 20)
+--
+-- We'd rather only do the (<=20) check. So we reify the predicate to make
+-- sure we only check for the most restrictive bound.
+data UpperLocBound = EndOfFile | EndLoc !BufPos
+
+instance Semigroup UpperLocBound where
+ EndOfFile <> l = l
+ l <> EndOfFile = l
+ EndLoc l1 <> EndLoc l2 = EndLoc (min l1 l2)
+
+instance Monoid UpperLocBound where
+ mempty = EndOfFile
+
+-- | Represents a predicate on the column number.
+--
+-- ColumnBound | Int -> Bool
+-- --------------+-----------------
+-- ColumnFrom n | (>=n)
+--
+-- The semigroup instance corresponds to (&&).
+--
+newtype ColumnBound = ColumnFrom Int -- n >= GHC.Types.SrcLoc.leftmostColumn
+
+instance Semigroup ColumnBound where
+ ColumnFrom n <> ColumnFrom m = ColumnFrom (max n m)
+
+instance Monoid ColumnBound where
+ mempty = ColumnFrom leftmostColumn
+
+
+{- *********************************************************************
+* *
+* AST manipulation utilities *
+* *
+********************************************************************* -}
+
+mkLHsDocTy :: LHsType GhcPs -> Maybe LHsDocString -> LHsType GhcPs
+mkLHsDocTy t Nothing = t
+mkLHsDocTy t (Just doc) = L (getLoc t) (HsDocTy noExtField t doc)
+
+getForAllTeleLoc :: HsForAllTelescope GhcPs -> SrcSpan
+getForAllTeleLoc tele =
+ foldr combineSrcSpans noSrcSpan $
+ case tele of
+ HsForAllVis{ hsf_vis_bndrs } -> map getLoc hsf_vis_bndrs
+ HsForAllInvis { hsf_invis_bndrs } -> map getLoc hsf_invis_bndrs
+
+-- | The inverse of 'partitionBindsAndSigs' that merges partitioned items back
+-- into a flat list. Elements are put back into the order in which they
+-- appeared in the original program before partitioning, using BufPos to order
+-- them.
+--
+-- Precondition (unchecked): the input lists are already sorted.
+flattenBindsAndSigs
+ :: (LHsBinds GhcPs, [LSig GhcPs], [LFamilyDecl GhcPs],
+ [LTyFamInstDecl GhcPs], [LDataFamInstDecl GhcPs], [LDocDecl])
+ -> [LHsDecl GhcPs]
+flattenBindsAndSigs (all_bs, all_ss, all_ts, all_tfis, all_dfis, all_docs) =
+ -- 'cmpBufSpan' is safe here with the following assumptions:
+ --
+ -- * 'LHsDecl' produced by 'decl_cls' in Parser.y always have a 'BufSpan'
+ -- * 'partitionBindsAndSigs' does not discard this 'BufSpan'
+ mergeListsBy cmpBufSpan [
+ mapLL (\b -> ValD noExtField b) (bagToList all_bs),
+ mapLL (\s -> SigD noExtField s) all_ss,
+ mapLL (\t -> TyClD noExtField (FamDecl noExtField t)) all_ts,
+ mapLL (\tfi -> InstD noExtField (TyFamInstD noExtField tfi)) all_tfis,
+ mapLL (\dfi -> InstD noExtField (DataFamInstD noExtField dfi)) all_dfis,
+ mapLL (\d -> DocD noExtField d) all_docs
+ ]
+
+{- *********************************************************************
+* *
+* General purpose utilities *
+* *
+********************************************************************* -}
+
+-- Cons an element to a list, if exists.
+mcons :: Maybe a -> [a] -> [a]
+mcons = maybe id (:)
+
+-- Map a function over a list of located items.
+mapLL :: (a -> b) -> [Located a] -> [Located b]
+mapLL f = map (mapLoc f)
+
+{- Note [Old solution: Haddock in the grammar]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In the past, Haddock comments were incorporated into the grammar (Parser.y).
+This led to excessive complexity and duplication.
+
+For example, here's the grammar production for types without documentation:
+
+ type : btype
+ | btype '->' ctype
+
+To support Haddock, we had to also maintain an additional grammar production
+for types with documentation on function arguments and function result:
+
+ typedoc : btype
+ | btype docprev
+ | docnext btype
+ | btype '->' ctypedoc
+ | btype docprev '->' ctypedoc
+ | docnext btype '->' ctypedoc
+
+Sometimes handling documentation comments during parsing led to bugs (#17561),
+and sometimes it simply made it hard to modify and extend the grammar.
+
+Another issue was that sometimes Haddock would fail to parse code
+that GHC could parse succesfully:
--- -----------------------------------------------------------------------------
--- Adding documentation to record fields (used in parsing).
+ class BadIndent where
+ f :: a -> Int
+ -- ^ comment
+ g :: a -> Int
-addFieldDoc :: LConDeclField GhcPs -> Maybe LHsDocString -> LConDeclField GhcPs
-addFieldDoc (L l fld) doc
- = L l (fld { cd_fld_doc = cd_fld_doc fld `mplus` doc })
+This declaration was accepted by ghc but rejected by ghc -haddock.
+-}
-addFieldDocs :: [LConDeclField GhcPs] -> Maybe LHsDocString -> [LConDeclField GhcPs]
-addFieldDocs [] _ = []
-addFieldDocs (x:xs) doc = addFieldDoc x doc : xs
+{- Note [Register keyword location]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+At the moment, 'addHaddock' erroneously associates some comments with
+constructs that are separated by a keyword. For example:
+ data Foo -- | Comment for MkFoo
+ where MkFoo :: Foo
-addConDoc :: LConDecl GhcPs -> Maybe LHsDocString -> LConDecl GhcPs
-addConDoc decl Nothing = decl
-addConDoc (L p c) doc = L p $ case c of
- ConDeclH98 { con_doc = old_doc } -> c { con_doc = old_doc `mplus` doc }
- ConDeclGADT { con_doc = old_doc } -> c { con_doc = old_doc `mplus` doc }
- XConDecl x@(ConDeclGADTPrefixPs { con_gp_doc = old_doc }) ->
- XConDecl (x { con_gp_doc = old_doc `mplus` doc })
+The issue stems from the lack of location information for keywords. We could
+utilize API Annotations for this purpose, but not without modification. For
+example, API Annotations operate on RealSrcSpan, whereas we need BufSpan.
-addConDocs :: [LConDecl GhcPs] -> Maybe LHsDocString -> [LConDecl GhcPs]
-addConDocs [] _ = []
-addConDocs [x] doc = [addConDoc x doc]
-addConDocs (x:xs) doc = x : addConDocs xs doc
+Also, there's work towards making API Annotations available in-tree (not in
+a separate Map), see #17638. This change should make the fix very easy (it
+is not as easy with the current design).
-addConDocFirst :: [LConDecl GhcPs] -> Maybe LHsDocString -> [LConDecl GhcPs]
-addConDocFirst [] _ = []
-addConDocFirst (x:xs) doc = addConDoc x doc : xs
+See also testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.hs
+-}
diff --git a/compiler/GHC/Tc/Module.hs b/compiler/GHC/Tc/Module.hs
index 0145c6c776..277a6fec7d 100644
--- a/compiler/GHC/Tc/Module.hs
+++ b/compiler/GHC/Tc/Module.hs
@@ -206,7 +206,7 @@ tcRnModuleTcRnM :: HscEnv
tcRnModuleTcRnM hsc_env mod_sum
(HsParsedModule {
hpm_module =
- (L loc (HsModule maybe_mod export_ies
+ (L loc (HsModule _ maybe_mod export_ies
import_decls local_decls mod_deprec
maybe_doc_hdr)),
hpm_src_files = src_files
diff --git a/compiler/GHC/ThToHs.hs b/compiler/GHC/ThToHs.hs
index 58add2b135..82ea8b97fe 100644
--- a/compiler/GHC/ThToHs.hs
+++ b/compiler/GHC/ThToHs.hs
@@ -270,7 +270,7 @@ cvtDec (ClassD ctxt cl tvs fds decs)
<+> text "are not allowed:")
$$ (Outputable.ppr adts'))
; returnJustL $ TyClD noExtField $
- ClassDecl { tcdCExt = noExtField
+ ClassDecl { tcdCExt = NoLayoutInfo
, tcdCtxt = cxt', tcdLName = tc', tcdTyVars = tvs'
, tcdFixity = Prefix
, tcdFDs = fds', tcdSigs = Hs.mkClassOpSigs sigs'
diff --git a/compiler/GHC/Types/SrcLoc.hs b/compiler/GHC/Types/SrcLoc.hs
index 00bf00ac2c..2ac2a13b04 100644
--- a/compiler/GHC/Types/SrcLoc.hs
+++ b/compiler/GHC/Types/SrcLoc.hs
@@ -69,7 +69,9 @@ module GHC.Types.SrcLoc (
-- * StringBuffer locations
BufPos(..),
+ getBufPos,
BufSpan(..),
+ getBufSpan,
-- * Located
Located,
@@ -88,10 +90,11 @@ module GHC.Types.SrcLoc (
mapLoc,
-- ** Combining and comparing Located values
- eqLocated, cmpLocated, combineLocs, addCLoc,
+ eqLocated, cmpLocated, cmpBufSpan,
+ combineLocs, addCLoc,
leftmost_smallest, leftmost_largest, rightmost_smallest,
- spans, isSubspanOf, isRealSubspanOf, sortLocated,
- sortRealLocated,
+ spans, isSubspanOf, isRealSubspanOf,
+ sortLocated, sortRealLocated,
lookupSrcLoc, lookupSrcSpan,
liftL,
@@ -106,6 +109,10 @@ module GHC.Types.SrcLoc (
psSpanEnd,
mkSrcSpanPs,
+ -- * Layout information
+ LayoutInfo(..),
+ leftmostColumn
+
) where
import GHC.Prelude
@@ -122,6 +129,7 @@ import Data.Data
import Data.List (sortBy, intercalate)
import Data.Function (on)
import qualified Data.Map as Map
+import qualified Data.Semigroup
{-
************************************************************************
@@ -143,13 +151,77 @@ data RealSrcLoc
{-# UNPACK #-} !Int -- column number, begins at 1
deriving (Eq, Ord)
--- | 0-based index identifying the raw location in the StringBuffer.
+-- | 0-based offset identifying the raw location in the 'StringBuffer'.
+--
+-- The lexer increments the 'BufPos' every time a character (UTF-8 code point)
+-- is read from the input buffer. As UTF-8 is a variable-length encoding and
+-- 'StringBuffer' needs a byte offset for indexing, a 'BufPos' cannot be used
+-- for indexing.
+--
+-- The parser guarantees that 'BufPos' are monotonic. See #17632. This means
+-- that syntactic constructs that appear later in the 'StringBuffer' are guaranteed to
+-- have a higher 'BufPos'. Constrast that with 'RealSrcLoc', which does *not* make the
+-- analogous guarantee about higher line/column numbers.
+--
+-- This is due to #line and {-# LINE ... #-} pragmas that can arbitrarily
+-- modify 'RealSrcLoc'. Notice how 'setSrcLoc' and 'resetAlrLastLoc' in
+-- "GHC.Parser.Lexer" update 'PsLoc', modifying 'RealSrcLoc' but preserving
+-- 'BufPos'.
+--
+-- Monotonicity makes 'BufPos' useful to determine the order in which syntactic
+-- elements appear in the source. Consider this example (haddockA041 in the test suite):
+--
+-- haddockA041.hs
+-- {-# LANGUAGE CPP #-}
+-- -- | Module header documentation
+-- module Comments_and_CPP_include where
+-- #include "IncludeMe.hs"
+--
+-- IncludeMe.hs:
+-- -- | Comment on T
+-- data T = MkT -- ^ Comment on MkT
+--
+-- After the C preprocessor runs, the 'StringBuffer' will contain a program that
+-- looks like this (unimportant lines at the beginning removed):
+--
+-- # 1 "haddockA041.hs"
+-- {-# LANGUAGE CPP #-}
+-- -- | Module header documentation
+-- module Comments_and_CPP_include where
+-- # 1 "IncludeMe.hs" 1
+-- -- | Comment on T
+-- data T = MkT -- ^ Comment on MkT
+-- # 7 "haddockA041.hs" 2
+--
+-- The line pragmas inserted by CPP make the error messages more informative.
+-- The downside is that we can't use RealSrcLoc to determine the ordering of
+-- syntactic elements.
+--
+-- With RealSrcLoc, we have the following location information recorded in the AST:
+-- * The module name is located at haddockA041.hs:3:8-31
+-- * The Haddock comment "Comment on T" is located at IncludeMe:1:1-17
+-- * The data declaration is located at IncludeMe.hs:2:1-32
--
--- Unlike 'RealSrcLoc', it is not affected by #line and {-# LINE ... #-}
--- pragmas. In particular, notice how 'setSrcLoc' and 'resetAlrLastLoc' in
--- "GHC.Parser.Lexer" update 'PsLoc' preserving 'BufPos'.
+-- Is the Haddock comment located between the module name and the data
+-- declaration? This is impossible to tell because the locations are not
+-- comparable; they even refer to different files.
--
--- The parser guarantees that 'BufPos' are monotonic. See #17632.
+-- On the other hand, with 'BufPos', we have the following location information:
+-- * The module name is located at 846-870
+-- * The Haddock comment "Comment on T" is located at 898-915
+-- * The data declaration is located at 916-928
+--
+-- Aside: if you're wondering why the numbers are so high, try running
+-- @ghc -E haddockA041.hs@
+-- and see the extra fluff that CPP inserts at the start of the file.
+--
+-- For error messages, 'BufPos' is not useful at all. On the other hand, this is
+-- exactly what we need to determine the order of syntactic elements:
+-- 870 < 898, therefore the Haddock comment appears *after* the module name.
+-- 915 < 916, therefore the Haddock comment appears *before* the data declaration.
+--
+-- We use 'BufPos' in in GHC.Parser.PostProcess.Haddock to associate Haddock
+-- comments with parts of the AST using location information (#17544).
newtype BufPos = BufPos { bufPos :: Int }
deriving (Eq, Ord, Show)
@@ -173,6 +245,10 @@ mkSrcLoc x line col = RealSrcLoc (mkRealSrcLoc x line col) Nothing
mkRealSrcLoc :: FastString -> Int -> Int -> RealSrcLoc
mkRealSrcLoc x line col = SrcLoc x line col
+getBufPos :: SrcLoc -> Maybe BufPos
+getBufPos (RealSrcLoc _ mbpos) = mbpos
+getBufPos (UnhelpfulLoc _) = Nothing
+
-- | Built-in "bad" 'SrcLoc' values for particular locations
noSrcLoc, generatedSrcLoc, interactiveSrcLoc :: SrcLoc
noSrcLoc = UnhelpfulLoc (fsLit "<no location info>")
@@ -298,6 +374,10 @@ data BufSpan =
BufSpan { bufSpanStart, bufSpanEnd :: {-# UNPACK #-} !BufPos }
deriving (Eq, Ord, Show)
+instance Semigroup BufSpan where
+ BufSpan start1 end1 <> BufSpan start2 end2 =
+ BufSpan (min start1 start2) (max end1 end2)
+
-- | Source Span
--
-- A 'SrcSpan' identifies either a specific portion of a text file
@@ -352,6 +432,10 @@ instance ToJson RealSrcSpan where
instance NFData SrcSpan where
rnf x = x `seq` ()
+getBufSpan :: SrcSpan -> Maybe BufSpan
+getBufSpan (RealSrcSpan _ mbspan) = mbspan
+getBufSpan (UnhelpfulSpan _) = Nothing
+
-- | Built-in "bad" 'SrcSpan's for common sources of location uncertainty
noSrcSpan, generatedSrcSpan, wiredInSrcSpan, interactiveSrcSpan :: SrcSpan
noSrcSpan = UnhelpfulSpan UnhelpfulNoLocationInfo
@@ -674,6 +758,17 @@ eqLocated a b = unLoc a == unLoc b
cmpLocated :: Ord a => GenLocated l a -> GenLocated l a -> Ordering
cmpLocated a b = unLoc a `compare` unLoc b
+-- | Compare the 'BufSpan' of two located things.
+--
+-- Precondition: both operands have an associated 'BufSpan'.
+cmpBufSpan :: HasDebugCallStack => Located a -> Located a -> Ordering
+cmpBufSpan (L l1 _) (L l2 _)
+ | Just a <- getBufSpan l1
+ , Just b <- getBufSpan l2
+ = compare a b
+
+ | otherwise = panic "cmpBufSpan: no BufSpan"
+
instance (Outputable l, Outputable e) => Outputable (GenLocated l e) where
ppr (L l e) = -- TODO: We can't do this since Located was refactored into
-- GenLocated:
@@ -768,3 +863,33 @@ psSpanEnd (PsSpan r b) = PsLoc (realSrcSpanEnd r) (bufSpanEnd b)
mkSrcSpanPs :: PsSpan -> SrcSpan
mkSrcSpanPs (PsSpan r b) = RealSrcSpan r (Just b)
+
+-- | Layout information for declarations.
+data LayoutInfo =
+
+ -- | Explicit braces written by the user.
+ --
+ -- @
+ -- class C a where { foo :: a; bar :: a }
+ -- @
+ ExplicitBraces
+ |
+ -- | Virtual braces inserted by the layout algorithm.
+ --
+ -- @
+ -- class C a where
+ -- foo :: a
+ -- bar :: a
+ -- @
+ VirtualBraces
+ !Int -- ^ Layout column (indentation level, begins at 1)
+ |
+ -- | Empty or compiler-generated blocks do not have layout information
+ -- associated with them.
+ NoLayoutInfo
+
+ deriving (Eq, Ord, Show, Data)
+
+-- | Indentation level is 1-indexed, so the leftmost column is 1.
+leftmostColumn :: Int
+leftmostColumn = 1
diff --git a/compiler/GHC/Utils/Misc.hs b/compiler/GHC/Utils/Misc.hs
index 6f0c0a6aa5..e0ef6abd0a 100644
--- a/compiler/GHC/Utils/Misc.hs
+++ b/compiler/GHC/Utils/Misc.hs
@@ -5,6 +5,7 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
@@ -49,9 +50,13 @@ module GHC.Utils.Misc (
chunkList,
changeLast,
+ mapLastM,
whenNonEmpty,
+ mergeListsBy,
+ isSortedBy,
+
-- * Tuples
fstOf3, sndOf3, thdOf3,
firstM, first3M, secondM,
@@ -601,10 +606,65 @@ changeLast [] _ = panic "changeLast"
changeLast [_] x = [x]
changeLast (x:xs) x' = x : changeLast xs x'
+-- | Apply an effectful function to the last list element.
+-- Assumes a non-empty list (panics otherwise).
+mapLastM :: Functor f => (a -> f a) -> [a] -> f [a]
+mapLastM _ [] = panic "mapLastM: empty list"
+mapLastM f [x] = (\x' -> [x']) <$> f x
+mapLastM f (x:xs) = (x:) <$> mapLastM f xs
+
whenNonEmpty :: Applicative m => [a] -> (NonEmpty a -> m ()) -> m ()
whenNonEmpty [] _ = pure ()
whenNonEmpty (x:xs) f = f (x :| xs)
+-- | Merge an unsorted list of sorted lists, for example:
+--
+-- > mergeListsBy compare [ [2,5,15], [1,10,100] ] = [1,2,5,10,15,100]
+--
+-- \( O(n \log{} k) \)
+mergeListsBy :: forall a. (a -> a -> Ordering) -> [[a]] -> [a]
+mergeListsBy cmp lists | debugIsOn, not (all sorted lists) =
+ -- When debugging is on, we check that the input lists are sorted.
+ panic "mergeListsBy: input lists must be sorted"
+ where sorted = isSortedBy cmp
+mergeListsBy cmp all_lists = merge_lists all_lists
+ where
+ -- Implements "Iterative 2-Way merge" described at
+ -- https://en.wikipedia.org/wiki/K-way_merge_algorithm
+
+ -- Merge two sorted lists into one in O(n).
+ merge2 :: [a] -> [a] -> [a]
+ merge2 [] ys = ys
+ merge2 xs [] = xs
+ merge2 (x:xs) (y:ys) =
+ case cmp x y of
+ GT -> y : merge2 (x:xs) ys
+ _ -> x : merge2 xs (y:ys)
+
+ -- Merge the first list with the second, the third with the fourth, and so
+ -- on. The output has half as much lists as the input.
+ merge_neighbours :: [[a]] -> [[a]]
+ merge_neighbours [] = []
+ merge_neighbours [xs] = [xs]
+ merge_neighbours (xs : ys : lists) =
+ merge2 xs ys : merge_neighbours lists
+
+ -- Since 'merge_neighbours' halves the amount of lists in each iteration,
+ -- we perform O(log k) iteration. Each iteration is O(n). The total running
+ -- time is therefore O(n log k).
+ merge_lists :: [[a]] -> [a]
+ merge_lists lists =
+ case merge_neighbours lists of
+ [] -> []
+ [xs] -> xs
+ lists' -> merge_lists lists'
+
+isSortedBy :: (a -> a -> Ordering) -> [a] -> Bool
+isSortedBy cmp = sorted
+ where
+ sorted [] = True
+ sorted [_] = True
+ sorted (x:y:xs) = cmp x y /= GT && sorted (y:xs)
{-
************************************************************************
* *
diff --git a/docs/users_guide/8.12.1-notes.rst b/docs/users_guide/8.12.1-notes.rst
index d393c6a381..1fe2e49894 100644
--- a/docs/users_guide/8.12.1-notes.rst
+++ b/docs/users_guide/8.12.1-notes.rst
@@ -290,6 +290,46 @@ Arrow notation
within 0.5 -< x
... |)
+Haddock
+~~~~~~~
+
+- Parsing is now more robust to insufficiently indented Haddock comments::
+
+ class C a where
+ f :: a -> a
+ -- ^ This comment used to trigger a parse error
+ g :: a -> a
+
+- :ghc-flag:`-Winvalid-haddock` is a new warning that reports discarded Haddock
+ comments that cannot be associated with AST elements::
+
+ myValue =
+ -- | Invalid (discarded) comment in an expression
+ 2 + 2
+
+- When faced with several comments for a data constructor or a data constructor
+ field, Haddock now picks the first one instead of the last one. The
+ extraneous comment is reported as invalid when :ghc-flag:`-Winvalid-haddock`
+ is enabled::
+
+ data T
+ -- | First comment
+ = MkT
+ -- ^ Second comment (rejected)
+
+
+- Haddock is now more relaxed about the placement of comments in types relative
+ to the function arrow ``->``, allowing more formatting styles::
+
+ f :: Int -> -- ^ comment on Int (no longer a parse error)
+ Bool -- ^ comment on Bool
+
+- Haddock can now parse the documentation comment for the first declaration in
+ a module without a module header (:ghc-ticket:`17561`)::
+
+ -- | This comment used to trigger a parse error
+ main = putStrLn "Hello"
+
``ghc-prim`` library
~~~~~~~~~~~~~~~~~~~~
@@ -330,6 +370,10 @@ Arrow notation
erased, and their ``exceptions``-alternatives are meant to be used in the
GHC code instead.
+- ``parseModule`` is now the only parser entry point that deals with Haddock
+ comments. The other entry points (``parseDeclaration``, ``parseExpression``,
+ etc) do not insert the Haddock comments into the AST.
+
``base`` library
~~~~~~~~~~~~~~~~
diff --git a/docs/users_guide/using-warnings.rst b/docs/users_guide/using-warnings.rst
index 256d143f45..a83cc6837e 100644
--- a/docs/users_guide/using-warnings.rst
+++ b/docs/users_guide/using-warnings.rst
@@ -1762,6 +1762,25 @@ of ``-W(no-)*``.
You may want to enable this warning on a clean build or enable :ghc-flag:`-fforce-recomp`
in order to get reliable results.
+.. ghc-flag:: -Winvalid-haddock
+ :shortdesc: warn when a Haddock comment occurs in an invalid position
+ :type: dynamic
+ :category:
+
+ :since: 8.12
+
+ When the ``-haddock`` option is enabled, GHC collects documentation
+ comments and associates them with declarations, function arguments, data
+ constructors, and other syntactic elements. Documentation comments in
+ invalid positions are discarded::
+
+ myValue =
+ -- | Invalid (discarded) comment in an expression
+ 2 + 2
+
+ This warning informs you about discarded documentation comments.
+ It has no effect when :ghc-flag:`-haddock` is disabled.
+
If you're feeling really paranoid, the :ghc-flag:`-dcore-lint` option is a good choice.
It turns on heavyweight intra-pass sanity-checking within GHC. (It checks GHC's
sanity, not yours.)
diff --git a/testsuite/tests/ghc-api/T11579.hs b/testsuite/tests/ghc-api/T11579.hs
index 7ea08c9216..9f1cc41f92 100644
--- a/testsuite/tests/ghc-api/T11579.hs
+++ b/testsuite/tests/ghc-api/T11579.hs
@@ -5,6 +5,7 @@ import GHC
import GHC.Data.StringBuffer
import GHC.Parser.Lexer
import GHC.Types.SrcLoc
+import Data.Foldable (toList)
main :: IO ()
main = do
@@ -13,14 +14,14 @@ main = do
let stringBuffer = stringToStringBuffer "-- $bar some\n-- named chunk"
loc = mkRealSrcLoc (mkFastString "Foo.hs") 1 1
- token <- runGhc (Just libdir) $ do
+ hdk_comments <- runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
let pstate = mkPState (dflags `gopt_set` Opt_Haddock) stringBuffer loc
case unP (lexer False return) pstate of
- POk _ token -> return (unLoc token)
- _ -> error "No token"
+ POk s (L _ ITeof) -> return (map unLoc (toList (hdk_comments s)))
+ _ -> error "No token"
-- #11579
-- Expected: "ITdocCommentNamed "bar some\n named chunk"
-- Actual (with ghc-8.0.1-rc2): "ITdocCommentNamed "bar some"
- print token
+ mapM_ print hdk_comments
diff --git a/testsuite/tests/ghc-api/T11579.stdout b/testsuite/tests/ghc-api/T11579.stdout
index 7603e535e7..24f3bf52e5 100644
--- a/testsuite/tests/ghc-api/T11579.stdout
+++ b/testsuite/tests/ghc-api/T11579.stdout
@@ -1 +1 @@
-ITdocCommentNamed "bar some\n named chunk"
+HdkCommentNamed "bar" (HsDocString " some\n named chunk")
diff --git a/testsuite/tests/ghc-api/annotations/comments.stdout b/testsuite/tests/ghc-api/annotations/comments.stdout
index 06273ba1e6..e5ff216fb0 100644
--- a/testsuite/tests/ghc-api/annotations/comments.stdout
+++ b/testsuite/tests/ghc-api/annotations/comments.stdout
@@ -1,12 +1,11 @@
[
-( CommentsTest.hs:11:1-33 =
-[(CommentsTest.hs:11:1-33,AnnDocCommentNext " The function @foo@ does blah")])
-
( CommentsTest.hs:(12,7)-(15,14) =
[(CommentsTest.hs:14:15-24,AnnLineComment "-- value 2")])
( <no location info> =
-[(CommentsTest.hs:(3,1)-(7,2),AnnBlockComment "{-\nAn opening comment\n {- with a nested one -}\n {-# nested PRAGMA #-}\n-}"),
+[(CommentsTest.hs:11:1-33,AnnDocCommentNext " The function @foo@ does blah"),
+
+(CommentsTest.hs:(3,1)-(7,2),AnnBlockComment "{-\nAn opening comment\n {- with a nested one -}\n {-# nested PRAGMA #-}\n-}"),
(CommentsTest.hs:1:1-31,AnnBlockComment "{-# LANGUAGE DeriveFoldable #-}")])
]
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/T11768.hs b/testsuite/tests/haddock/should_compile_flag_haddock/T11768.hs
index 5e7369cdc0..52899930be 100644
--- a/testsuite/tests/haddock/should_compile_flag_haddock/T11768.hs
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/T11768.hs
@@ -1,4 +1,5 @@
{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
@@ -7,6 +8,11 @@ module T11768 where
class C a b
+class D a
+
+newtype DWrapper a = DWrap a
+instance D (DWrapper a)
+
data Foo = Foo
deriving Eq -- ^ Documenting a single type
@@ -15,6 +21,7 @@ data Bar = Bar
, Ord
)
deriving anyclass ( forall a. C a {- ^ Documenting forall type -} )
+ deriving D {- ^ Documenting deriving via -} via DWrapper Bar
-- | Documenting a standalone deriving instance
deriving instance Read Bar
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/T11768.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/T11768.stderr
index 6de1b2b851..5fe63362b1 100644
--- a/testsuite/tests/haddock/should_compile_flag_haddock/T11768.stderr
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/T11768.stderr
@@ -2,6 +2,9 @@
==================== Parser ====================
module T11768 where
class C a b
+class D a
+newtype DWrapper a = DWrap a
+instance D (DWrapper a)
data Foo
= Foo
deriving Eq " Documenting a single type"
@@ -9,6 +12,7 @@ data Bar
= Bar
deriving (Eq " Documenting one of multiple types", Ord)
deriving anyclass (forall a. C a " Documenting forall type ")
+ deriving D " Documenting deriving via " via DWrapper Bar
<document comment>
deriving instance Read Bar
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/T17544.hs b/testsuite/tests/haddock/should_compile_flag_haddock/T17544.hs
new file mode 100644
index 0000000000..884bb2f495
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/T17544.hs
@@ -0,0 +1,56 @@
+{-# LANGUAGE TypeFamilies, GADTSyntax #-}
+
+module T17544 where
+
+class C1 a where
+ f1 :: a -> Int
+ -- ^ comment on Int
+
+class C2 a where
+ f2 :: a -> Int
+ -- ^ comment on f2
+
+class C3 a where
+ f3 :: a -> Int
+-- ^ comment on C3
+
+class C4 a where
+ f4 :: a -> Int
+-- ^ comment
+ g4 :: a -> Int
+
+class C5 a where { data D5 a }
+instance C5 Int where
+ data D5 Int where
+ MkD5 :: D5 Int
+ -- ^ comment on D5 Int
+
+class C6 a where { data D6 a }
+instance C6 Int where
+ data D6 Int where
+ MkD6 :: D6 Int
+ -- ^ comment on MkD6
+
+class C7 a where { data D7 a }
+instance C7 Int where
+ data D7 Int where
+ MkD7 :: D7 Int
+ -- ^ comment on data instance D7 Int
+
+class C8 a where { data D8 a }
+instance C8 Int where
+ data D8 Int where
+ MkD8 :: D8 Int
+ -- ^ comment on data instance D8 Int
+
+class C9 a where { data D9 a }
+instance C9 Int where
+ data D9 Int where
+ MkD9 :: D9 Int
+ -- ^ comment on class instance C9 Int
+
+class C10 a where { data D10 a }
+instance C10 Int where
+ data D10 Int where
+ MkD10 :: D10 Int
+-- ^ comment on class instance C10 Int
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
new file mode 100644
index 0000000000..863015241f
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
@@ -0,0 +1,1090 @@
+
+==================== Parser AST ====================
+
+({ T17544.hs:1:1 }
+ (HsModule
+ (VirtualBraces
+ (1))
+ (Just
+ ({ T17544.hs:3:8-13 }
+ {ModuleName: T17544}))
+ (Nothing)
+ []
+ [({ T17544.hs:(5,1)-(6,16) }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (VirtualBraces
+ (3))
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:5:7-8 }
+ (Unqual
+ {OccName: C1}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:5:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:5:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ [({ T17544.hs:6:3-16 }
+ (ClassOpSig
+ (NoExtField)
+ (False)
+ [({ T17544.hs:6:3-4 }
+ (Unqual
+ {OccName: f1}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:6:9-16 }
+ (HsFunTy
+ (NoExtField)
+ (HsUnrestrictedArrow)
+ ({ T17544.hs:6:9 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:6:9 }
+ (Unqual
+ {OccName: a}))))
+ ({ T17544.hs:6:14-16 }
+ (HsDocTy
+ (NoExtField)
+ ({ T17544.hs:6:14-16 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:6:14-16 }
+ (Unqual
+ {OccName: Int}))))
+ ({ T17544.hs:7:5-23 }
+ (HsDocString
+ " comment on Int")))))))))]
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [])))
+ ,({ T17544.hs:(9,1)-(10,16) }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (VirtualBraces
+ (3))
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:9:7-8 }
+ (Unqual
+ {OccName: C2}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:9:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:9:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ [({ T17544.hs:10:3-16 }
+ (ClassOpSig
+ (NoExtField)
+ (False)
+ [({ T17544.hs:10:3-4 }
+ (Unqual
+ {OccName: f2}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:10:9-16 }
+ (HsFunTy
+ (NoExtField)
+ (HsUnrestrictedArrow)
+ ({ T17544.hs:10:9 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:10:9 }
+ (Unqual
+ {OccName: a}))))
+ ({ T17544.hs:10:14-16 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:10:14-16 }
+ (Unqual
+ {OccName: Int})))))))))]
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [({ T17544.hs:11:3-20 }
+ (DocCommentPrev
+ (HsDocString
+ " comment on f2")))])))
+ ,({ T17544.hs:(13,1)-(14,16) }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (VirtualBraces
+ (3))
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:13:7-8 }
+ (Unqual
+ {OccName: C3}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:13:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:13:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ [({ T17544.hs:14:3-16 }
+ (ClassOpSig
+ (NoExtField)
+ (False)
+ [({ T17544.hs:14:3-4 }
+ (Unqual
+ {OccName: f3}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:14:9-16 }
+ (HsFunTy
+ (NoExtField)
+ (HsUnrestrictedArrow)
+ ({ T17544.hs:14:9 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:14:9 }
+ (Unqual
+ {OccName: a}))))
+ ({ T17544.hs:14:14-16 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:14:14-16 }
+ (Unqual
+ {OccName: Int})))))))))]
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [])))
+ ,({ T17544.hs:15:1-18 }
+ (DocD
+ (NoExtField)
+ (DocCommentPrev
+ (HsDocString
+ " comment on C3"))))
+ ,({ T17544.hs:(17,1)-(20,16) }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (VirtualBraces
+ (3))
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:17:7-8 }
+ (Unqual
+ {OccName: C4}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:17:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:17:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ [({ T17544.hs:18:3-16 }
+ (ClassOpSig
+ (NoExtField)
+ (False)
+ [({ T17544.hs:18:3-4 }
+ (Unqual
+ {OccName: f4}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:18:9-16 }
+ (HsFunTy
+ (NoExtField)
+ (HsUnrestrictedArrow)
+ ({ T17544.hs:18:9 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:18:9 }
+ (Unqual
+ {OccName: a}))))
+ ({ T17544.hs:18:14-16 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:18:14-16 }
+ (Unqual
+ {OccName: Int})))))))))
+ ,({ T17544.hs:20:3-16 }
+ (ClassOpSig
+ (NoExtField)
+ (False)
+ [({ T17544.hs:20:3-4 }
+ (Unqual
+ {OccName: g4}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:20:9-16 }
+ (HsFunTy
+ (NoExtField)
+ (HsUnrestrictedArrow)
+ ({ T17544.hs:20:9 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:20:9 }
+ (Unqual
+ {OccName: a}))))
+ ({ T17544.hs:20:14-16 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:20:14-16 }
+ (Unqual
+ {OccName: Int})))))))))]
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [])))
+ ,({ T17544.hs:22:1-30 }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (ExplicitBraces)
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:22:7-8 }
+ (Unqual
+ {OccName: C5}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:22:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:22:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ []
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ [({ T17544.hs:22:20-28 }
+ (FamilyDecl
+ (NoExtField)
+ (DataFamily)
+ ({ T17544.hs:22:25-26 }
+ (Unqual
+ {OccName: D5}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:22:28 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:22:28 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ ({ <no location info> }
+ (NoSig
+ (NoExtField)))
+ (Nothing)))]
+ []
+ [])))
+ ,({ T17544.hs:(23,1)-(25,18) }
+ (InstD
+ (NoExtField)
+ (ClsInstD
+ (NoExtField)
+ (ClsInstDecl
+ (NoExtField)
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:23:10-15 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:23:10-11 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:23:10-11 }
+ (Unqual
+ {OccName: C5}))))
+ ({ T17544.hs:23:13-15 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:23:13-15 }
+ (Unqual
+ {OccName: Int})))))))
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [({ T17544.hs:(24,3)-(25,18) }
+ (DataFamInstDecl
+ (HsIB
+ (NoExtField)
+ (FamEqn
+ (NoExtField)
+ ({ T17544.hs:24:8-9 }
+ (Unqual
+ {OccName: D5}))
+ (Nothing)
+ [(HsValArg
+ ({ T17544.hs:24:11-13 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:24:11-13 }
+ (Unqual
+ {OccName: Int})))))]
+ (Prefix)
+ (HsDataDefn
+ (NoExtField)
+ (DataType)
+ ({ <no location info> }
+ [])
+ (Nothing)
+ (Nothing)
+ [({ T17544.hs:25:5-18 }
+ (XConDecl
+ (ConDeclGADTPrefixPs
+ [({ T17544.hs:25:5-8 }
+ (Unqual
+ {OccName: MkD5}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:25:13-18 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:25:13-14 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:25:13-14 }
+ (Unqual
+ {OccName: D5}))))
+ ({ T17544.hs:25:16-18 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:25:16-18 }
+ (Unqual
+ {OccName: Int})))))))
+ (Nothing))))]
+ ({ <no location info> }
+ []))))))]
+ (Nothing)))))
+ ,({ T17544.hs:28:1-30 }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (ExplicitBraces)
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:28:7-8 }
+ (Unqual
+ {OccName: C6}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:28:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:28:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ []
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ [({ T17544.hs:28:20-28 }
+ (FamilyDecl
+ (NoExtField)
+ (DataFamily)
+ ({ T17544.hs:28:25-26 }
+ (Unqual
+ {OccName: D6}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:28:28 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:28:28 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ ({ <no location info> }
+ (NoSig
+ (NoExtField)))
+ (Nothing)))]
+ []
+ [])))
+ ,({ T17544.hs:(29,1)-(31,18) }
+ (InstD
+ (NoExtField)
+ (ClsInstD
+ (NoExtField)
+ (ClsInstDecl
+ (NoExtField)
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:29:10-15 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:29:10-11 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:29:10-11 }
+ (Unqual
+ {OccName: C6}))))
+ ({ T17544.hs:29:13-15 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:29:13-15 }
+ (Unqual
+ {OccName: Int})))))))
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [({ T17544.hs:(30,3)-(31,18) }
+ (DataFamInstDecl
+ (HsIB
+ (NoExtField)
+ (FamEqn
+ (NoExtField)
+ ({ T17544.hs:30:8-9 }
+ (Unqual
+ {OccName: D6}))
+ (Nothing)
+ [(HsValArg
+ ({ T17544.hs:30:11-13 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:30:11-13 }
+ (Unqual
+ {OccName: Int})))))]
+ (Prefix)
+ (HsDataDefn
+ (NoExtField)
+ (DataType)
+ ({ <no location info> }
+ [])
+ (Nothing)
+ (Nothing)
+ [({ T17544.hs:31:5-18 }
+ (XConDecl
+ (ConDeclGADTPrefixPs
+ [({ T17544.hs:31:5-8 }
+ (Unqual
+ {OccName: MkD6}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:31:13-18 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:31:13-14 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:31:13-14 }
+ (Unqual
+ {OccName: D6}))))
+ ({ T17544.hs:31:16-18 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:31:16-18 }
+ (Unqual
+ {OccName: Int})))))))
+ (Nothing))))]
+ ({ <no location info> }
+ []))))))]
+ (Nothing)))))
+ ,({ T17544.hs:34:1-30 }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (ExplicitBraces)
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:34:7-8 }
+ (Unqual
+ {OccName: C7}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:34:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:34:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ []
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ [({ T17544.hs:34:20-28 }
+ (FamilyDecl
+ (NoExtField)
+ (DataFamily)
+ ({ T17544.hs:34:25-26 }
+ (Unqual
+ {OccName: D7}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:34:28 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:34:28 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ ({ <no location info> }
+ (NoSig
+ (NoExtField)))
+ (Nothing)))]
+ []
+ [])))
+ ,({ T17544.hs:(35,1)-(37,18) }
+ (InstD
+ (NoExtField)
+ (ClsInstD
+ (NoExtField)
+ (ClsInstDecl
+ (NoExtField)
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:35:10-15 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:35:10-11 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:35:10-11 }
+ (Unqual
+ {OccName: C7}))))
+ ({ T17544.hs:35:13-15 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:35:13-15 }
+ (Unqual
+ {OccName: Int})))))))
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [({ T17544.hs:(36,3)-(37,18) }
+ (DataFamInstDecl
+ (HsIB
+ (NoExtField)
+ (FamEqn
+ (NoExtField)
+ ({ T17544.hs:36:8-9 }
+ (Unqual
+ {OccName: D7}))
+ (Nothing)
+ [(HsValArg
+ ({ T17544.hs:36:11-13 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:36:11-13 }
+ (Unqual
+ {OccName: Int})))))]
+ (Prefix)
+ (HsDataDefn
+ (NoExtField)
+ (DataType)
+ ({ <no location info> }
+ [])
+ (Nothing)
+ (Nothing)
+ [({ T17544.hs:37:5-18 }
+ (XConDecl
+ (ConDeclGADTPrefixPs
+ [({ T17544.hs:37:5-8 }
+ (Unqual
+ {OccName: MkD7}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:37:13-18 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:37:13-14 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:37:13-14 }
+ (Unqual
+ {OccName: D7}))))
+ ({ T17544.hs:37:16-18 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:37:16-18 }
+ (Unqual
+ {OccName: Int})))))))
+ (Nothing))))]
+ ({ <no location info> }
+ []))))))]
+ (Nothing)))))
+ ,({ T17544.hs:40:1-30 }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (ExplicitBraces)
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:40:7-8 }
+ (Unqual
+ {OccName: C8}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:40:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:40:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ []
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ [({ T17544.hs:40:20-28 }
+ (FamilyDecl
+ (NoExtField)
+ (DataFamily)
+ ({ T17544.hs:40:25-26 }
+ (Unqual
+ {OccName: D8}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:40:28 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:40:28 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ ({ <no location info> }
+ (NoSig
+ (NoExtField)))
+ (Nothing)))]
+ []
+ [])))
+ ,({ T17544.hs:(41,1)-(43,18) }
+ (InstD
+ (NoExtField)
+ (ClsInstD
+ (NoExtField)
+ (ClsInstDecl
+ (NoExtField)
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:41:10-15 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:41:10-11 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:41:10-11 }
+ (Unqual
+ {OccName: C8}))))
+ ({ T17544.hs:41:13-15 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:41:13-15 }
+ (Unqual
+ {OccName: Int})))))))
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [({ T17544.hs:(42,3)-(43,18) }
+ (DataFamInstDecl
+ (HsIB
+ (NoExtField)
+ (FamEqn
+ (NoExtField)
+ ({ T17544.hs:42:8-9 }
+ (Unqual
+ {OccName: D8}))
+ (Nothing)
+ [(HsValArg
+ ({ T17544.hs:42:11-13 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:42:11-13 }
+ (Unqual
+ {OccName: Int})))))]
+ (Prefix)
+ (HsDataDefn
+ (NoExtField)
+ (DataType)
+ ({ <no location info> }
+ [])
+ (Nothing)
+ (Nothing)
+ [({ T17544.hs:43:5-18 }
+ (XConDecl
+ (ConDeclGADTPrefixPs
+ [({ T17544.hs:43:5-8 }
+ (Unqual
+ {OccName: MkD8}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:43:13-18 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:43:13-14 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:43:13-14 }
+ (Unqual
+ {OccName: D8}))))
+ ({ T17544.hs:43:16-18 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:43:16-18 }
+ (Unqual
+ {OccName: Int})))))))
+ (Nothing))))]
+ ({ <no location info> }
+ []))))))]
+ (Nothing)))))
+ ,({ T17544.hs:46:1-30 }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (ExplicitBraces)
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:46:7-8 }
+ (Unqual
+ {OccName: C9}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:46:10 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:46:10 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ []
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ [({ T17544.hs:46:20-28 }
+ (FamilyDecl
+ (NoExtField)
+ (DataFamily)
+ ({ T17544.hs:46:25-26 }
+ (Unqual
+ {OccName: D9}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:46:28 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:46:28 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ ({ <no location info> }
+ (NoSig
+ (NoExtField)))
+ (Nothing)))]
+ []
+ [])))
+ ,({ T17544.hs:(47,1)-(49,18) }
+ (InstD
+ (NoExtField)
+ (ClsInstD
+ (NoExtField)
+ (ClsInstDecl
+ (NoExtField)
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:47:10-15 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:47:10-11 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:47:10-11 }
+ (Unqual
+ {OccName: C9}))))
+ ({ T17544.hs:47:13-15 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:47:13-15 }
+ (Unqual
+ {OccName: Int})))))))
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [({ T17544.hs:(48,3)-(49,18) }
+ (DataFamInstDecl
+ (HsIB
+ (NoExtField)
+ (FamEqn
+ (NoExtField)
+ ({ T17544.hs:48:8-9 }
+ (Unqual
+ {OccName: D9}))
+ (Nothing)
+ [(HsValArg
+ ({ T17544.hs:48:11-13 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:48:11-13 }
+ (Unqual
+ {OccName: Int})))))]
+ (Prefix)
+ (HsDataDefn
+ (NoExtField)
+ (DataType)
+ ({ <no location info> }
+ [])
+ (Nothing)
+ (Nothing)
+ [({ T17544.hs:49:5-18 }
+ (XConDecl
+ (ConDeclGADTPrefixPs
+ [({ T17544.hs:49:5-8 }
+ (Unqual
+ {OccName: MkD9}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:49:13-18 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:49:13-14 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:49:13-14 }
+ (Unqual
+ {OccName: D9}))))
+ ({ T17544.hs:49:16-18 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:49:16-18 }
+ (Unqual
+ {OccName: Int})))))))
+ (Nothing))))]
+ ({ <no location info> }
+ []))))))]
+ (Nothing)))))
+ ,({ T17544.hs:52:1-32 }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (ExplicitBraces)
+ ({ <no location info> }
+ [])
+ ({ T17544.hs:52:7-9 }
+ (Unqual
+ {OccName: C10}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:52:11 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:52:11 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ []
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ [({ T17544.hs:52:21-30 }
+ (FamilyDecl
+ (NoExtField)
+ (DataFamily)
+ ({ T17544.hs:52:26-28 }
+ (Unqual
+ {OccName: D10}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544.hs:52:30 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544.hs:52:30 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ ({ <no location info> }
+ (NoSig
+ (NoExtField)))
+ (Nothing)))]
+ []
+ [])))
+ ,({ T17544.hs:(53,1)-(55,20) }
+ (InstD
+ (NoExtField)
+ (ClsInstD
+ (NoExtField)
+ (ClsInstDecl
+ (NoExtField)
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:53:10-16 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:53:10-12 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:53:10-12 }
+ (Unqual
+ {OccName: C10}))))
+ ({ T17544.hs:53:14-16 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:53:14-16 }
+ (Unqual
+ {OccName: Int})))))))
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [({ T17544.hs:(54,3)-(55,20) }
+ (DataFamInstDecl
+ (HsIB
+ (NoExtField)
+ (FamEqn
+ (NoExtField)
+ ({ T17544.hs:54:8-10 }
+ (Unqual
+ {OccName: D10}))
+ (Nothing)
+ [(HsValArg
+ ({ T17544.hs:54:12-14 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:54:12-14 }
+ (Unqual
+ {OccName: Int})))))]
+ (Prefix)
+ (HsDataDefn
+ (NoExtField)
+ (DataType)
+ ({ <no location info> }
+ [])
+ (Nothing)
+ (Nothing)
+ [({ T17544.hs:55:5-20 }
+ (XConDecl
+ (ConDeclGADTPrefixPs
+ [({ T17544.hs:55:5-9 }
+ (Unqual
+ {OccName: MkD10}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544.hs:55:14-20 }
+ (HsAppTy
+ (NoExtField)
+ ({ T17544.hs:55:14-16 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:55:14-16 }
+ (Unqual
+ {OccName: D10}))))
+ ({ T17544.hs:55:18-20 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544.hs:55:18-20 }
+ (Unqual
+ {OccName: Int})))))))
+ (Nothing))))]
+ ({ <no location info> }
+ []))))))]
+ (Nothing)))))
+ ,({ T17544.hs:56:1-38 }
+ (DocD
+ (NoExtField)
+ (DocCommentPrev
+ (HsDocString
+ " comment on class instance C10 Int"))))]
+ (Nothing)
+ (Nothing)))
+
+
+
+T17544.hs:19:1: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
+
+T17544.hs:26:6: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
+
+T17544.hs:32:5: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
+
+T17544.hs:38:4: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
+
+T17544.hs:44:3: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
+
+T17544.hs:50:2: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.hs b/testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.hs
new file mode 100644
index 0000000000..4acf2af68d
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE GADTs #-}
+{-# OPTIONS -haddock -ddump-parsed-ast #-}
+
+-- Haddock comments in this test case should all be rejected, but they are not.
+--
+-- This is a known issue. Users should avoid writing comments in such
+-- positions, as a future fix will disallow them.
+--
+-- See Note [Register keyword location] in GHC.Parser.PostProcess.Haddock
+
+module
+ -- | Bad comment for the module
+ T17544_kw where
+
+data Foo -- | Bad comment for MkFoo
+ where MkFoo :: Foo
+
+newtype Bar -- | Bad comment for MkBar
+ where MkBar :: () -> Bar
+
+class Cls a
+ -- | Bad comment for clsmethod
+ where
+ clsmethod :: a
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
new file mode 100644
index 0000000000..9d45b6a86d
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
@@ -0,0 +1,154 @@
+
+==================== Parser AST ====================
+
+({ T17544_kw.hs:1:1 }
+ (HsModule
+ (VirtualBraces
+ (1))
+ (Just
+ ({ T17544_kw.hs:13:3-11 }
+ {ModuleName: T17544_kw}))
+ (Nothing)
+ []
+ [({ T17544_kw.hs:(15,1)-(16,20) }
+ (TyClD
+ (NoExtField)
+ (DataDecl
+ (NoExtField)
+ ({ T17544_kw.hs:15:6-8 }
+ (Unqual
+ {OccName: Foo}))
+ (HsQTvs
+ (NoExtField)
+ [])
+ (Prefix)
+ (HsDataDefn
+ (NoExtField)
+ (DataType)
+ ({ <no location info> }
+ [])
+ (Nothing)
+ (Nothing)
+ [({ T17544_kw.hs:16:9-20 }
+ (XConDecl
+ (ConDeclGADTPrefixPs
+ [({ T17544_kw.hs:16:9-13 }
+ (Unqual
+ {OccName: MkFoo}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544_kw.hs:16:18-20 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544_kw.hs:16:18-20 }
+ (Unqual
+ {OccName: Foo})))))
+ (Just
+ ({ T17544_kw.hs:15:10-35 }
+ (HsDocString
+ " Bad comment for MkFoo"))))))]
+ ({ <no location info> }
+ [])))))
+ ,({ T17544_kw.hs:(18,1)-(19,26) }
+ (TyClD
+ (NoExtField)
+ (DataDecl
+ (NoExtField)
+ ({ T17544_kw.hs:18:9-11 }
+ (Unqual
+ {OccName: Bar}))
+ (HsQTvs
+ (NoExtField)
+ [])
+ (Prefix)
+ (HsDataDefn
+ (NoExtField)
+ (NewType)
+ ({ <no location info> }
+ [])
+ (Nothing)
+ (Nothing)
+ [({ T17544_kw.hs:19:9-26 }
+ (XConDecl
+ (ConDeclGADTPrefixPs
+ [({ T17544_kw.hs:19:9-13 }
+ (Unqual
+ {OccName: MkBar}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544_kw.hs:19:18-26 }
+ (HsFunTy
+ (NoExtField)
+ (HsUnrestrictedArrow)
+ ({ T17544_kw.hs:19:18-19 }
+ (HsTupleTy
+ (NoExtField)
+ (HsBoxedOrConstraintTuple)
+ []))
+ ({ T17544_kw.hs:19:24-26 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544_kw.hs:19:24-26 }
+ (Unqual
+ {OccName: Bar})))))))
+ (Just
+ ({ T17544_kw.hs:18:13-38 }
+ (HsDocString
+ " Bad comment for MkBar"))))))]
+ ({ <no location info> }
+ [])))))
+ ,({ T17544_kw.hs:(21,1)-(24,18) }
+ (TyClD
+ (NoExtField)
+ (ClassDecl
+ (VirtualBraces
+ (5))
+ ({ <no location info> }
+ [])
+ ({ T17544_kw.hs:21:7-9 }
+ (Unqual
+ {OccName: Cls}))
+ (HsQTvs
+ (NoExtField)
+ [({ T17544_kw.hs:21:11 }
+ (UserTyVar
+ (NoExtField)
+ (())
+ ({ T17544_kw.hs:21:11 }
+ (Unqual
+ {OccName: a}))))])
+ (Prefix)
+ []
+ [({ T17544_kw.hs:24:5-18 }
+ (ClassOpSig
+ (NoExtField)
+ (False)
+ [({ T17544_kw.hs:24:5-13 }
+ (Unqual
+ {OccName: clsmethod}))]
+ (HsIB
+ (NoExtField)
+ ({ T17544_kw.hs:24:18 }
+ (HsTyVar
+ (NoExtField)
+ (NotPromoted)
+ ({ T17544_kw.hs:24:18 }
+ (Unqual
+ {OccName: a})))))))]
+ {Bag(Located (HsBind GhcPs)):
+ []}
+ []
+ []
+ [({ T17544_kw.hs:22:5-34 }
+ (DocCommentNext
+ (HsDocString
+ " Bad comment for clsmethod")))])))]
+ (Nothing)
+ (Just
+ ({ T17544_kw.hs:12:3-33 }
+ (HsDocString
+ " Bad comment for the module")))))
+
+
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/T8944.hs b/testsuite/tests/haddock/should_compile_flag_haddock/T8944.hs
new file mode 100644
index 0000000000..93ce9de99b
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/T8944.hs
@@ -0,0 +1,10 @@
+module T8944 where
+
+import Data.Maybe ()
+-- * whatever
+import Data.Functor ()
+
+data F = F () -- ^ Comment for the first argument
+ ()
+
+
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/T8944.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/T8944.stderr
new file mode 100644
index 0000000000..6a7e12e763
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/T8944.stderr
@@ -0,0 +1,11 @@
+
+==================== Parser ====================
+module T8944 where
+import Data.Maybe ()
+import Data.Functor ()
+data F = F () " Comment for the first argument" ()
+
+
+
+T8944.hs:4:1: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/all.T b/testsuite/tests/haddock/should_compile_flag_haddock/all.T
index c7b9d91c25..b35af797ce 100644
--- a/testsuite/tests/haddock/should_compile_flag_haddock/all.T
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/all.T
@@ -7,53 +7,58 @@
# When adding a new test here, think about adding it to the
# should_compile_noflag_haddock directory as well.
-test('haddockA001', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA002', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA003', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA004', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA005', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA006', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA007', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA008', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA009', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA010', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA011', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA012', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA013', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA014', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA015', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA016', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA017', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA018', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA019', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA020', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA021', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA022', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA023', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA024', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA025', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA026', normal, compile, ['-haddock -ddump-parsed -XRankNTypes'])
-test('haddockA027', normal, compile, ['-haddock -ddump-parsed -XRankNTypes'])
-test('haddockA028', normal, compile, ['-haddock -ddump-parsed -XTypeOperators'])
-test('haddockA029', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA030', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA031', normal, compile, ['-haddock -ddump-parsed -XExistentialQuantification'])
-test('haddockA032', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA035', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA036', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA037', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA038', normal, compile, ['-haddock -ddump-parsed'])
+test('haddockA001', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA002', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA003', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA004', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA005', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA006', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA007', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA008', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA009', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA010', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA011', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA012', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA013', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA014', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA015', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA016', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA017', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA018', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA019', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA020', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA021', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA022', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA023', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA024', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA025', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA026', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed -XRankNTypes'])
+test('haddockA027', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed -XRankNTypes'])
+test('haddockA028', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed -XTypeOperators'])
+test('haddockA029', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA030', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA031', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed -XExistentialQuantification'])
+test('haddockA032', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA035', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA036', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA037', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA038', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
# The tests below this line are not duplicated in
# should_compile_noflag_haddock.
-test('haddockA033', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA034', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA039', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA040', normal, compile, ['-haddock -ddump-parsed'])
-test('haddockA041', [extra_files(['IncludeMe.hs'])], compile, ['-haddock -ddump-parsed'])
-test('T10398', normal, compile, ['-haddock -ddump-parsed'])
-test('T11768', normal, compile, ['-haddock -ddump-parsed'])
-test('T15206', normal, compile, ['-haddock -ddump-parsed'])
-test('T16585', normal, compile, ['-haddock -ddump-parsed'])
-test('T17561', expect_broken(17561), compile, ['-haddock -ddump-parsed'])
+test('haddockA033', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA034', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA039', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA040', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('haddockA041', [extra_files(['IncludeMe.hs'])], compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('T10398', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('T11768', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('T15206', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('T16585', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('T17561', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('T17544', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed-ast'])
+test('T17544_kw', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed-ast'])
+test('haddockExtraDocs', normal, compile, ['-haddock -Winvalid-haddock'])
+test('haddockTySyn', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
+test('T8944', normal, compile, ['-haddock -Winvalid-haddock -ddump-parsed'])
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/haddockA022.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/haddockA022.stderr
index 6e6c5c6730..010ec9c069 100644
--- a/testsuite/tests/haddock/should_compile_flag_haddock/haddockA022.stderr
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/haddockA022.stderr
@@ -1,9 +1,19 @@
==================== Parser ====================
-main = print (test :: Int)
- where
- test = 0
- test2 = 1
- test3 = 2
+main
+ = print (test :: Int)
+ where
+ test = 0
+ test2 = 1
+ test3 = 2
+
+haddockA022.hs:4:5: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
+
+haddockA022.hs:6:5: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
+
+haddockA022.hs:10:5: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/haddockA030.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/haddockA030.stderr
index e09cfa2187..81b172ed80 100644
--- a/testsuite/tests/haddock/should_compile_flag_haddock/haddockA030.stderr
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/haddockA030.stderr
@@ -2,9 +2,13 @@
==================== Parser ====================
module ShouldCompile where
data A
- = " comment for A " A |
+ = " A comment that documents the first constructor" A |
" comment for B " B |
" comment for C " C |
D
+
+haddockA030.hs:7:5: warning: [-Winvalid-haddock]
+ Multiple Haddock comments for a single entity are not allowed.
+ The extraneous comment will be ignored.
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/haddockA033.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/haddockA033.stderr
index c1760c11fe..e3df0ec16f 100644
--- a/testsuite/tests/haddock/should_compile_flag_haddock/haddockA033.stderr
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/haddockA033.stderr
@@ -9,3 +9,9 @@ f 3 = 6
<document comment>
+
+haddockA033.hs:5:1: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
+
+haddockA033.hs:7:1: warning: [-Winvalid-haddock]
+ A Haddock comment cannot appear in this position and will be ignored.
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/haddockA041.hs b/testsuite/tests/haddock/should_compile_flag_haddock/haddockA041.hs
index fe9f7a24c4..3ba2c48fd9 100644
--- a/testsuite/tests/haddock/should_compile_flag_haddock/haddockA041.hs
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/haddockA041.hs
@@ -1,6 +1,4 @@
{-# LANGUAGE CPP #-}
-
-- | Module header documentation
module Comments_and_CPP_include where
-
#include "IncludeMe.hs"
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/haddockExtraDocs.hs b/testsuite/tests/haddock/should_compile_flag_haddock/haddockExtraDocs.hs
new file mode 100644
index 0000000000..dc91ab3126
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/haddockExtraDocs.hs
@@ -0,0 +1,25 @@
+module HaddockExtraDocs where
+
+data SomeField = SomeField
+
+data T1 =
+ MkT1
+ -- | Comment on SomeField
+ SomeField
+ -- ^ Another comment on SomeField? (rejected)
+
+data T2 =
+ MkT2 {
+ -- | Comment on SomeField
+ someField :: SomeField
+ } -- ^ Another comment on SomeField? (rejected)
+
+data T3 =
+ -- | Comment on MkT3
+ MkT3
+ -- ^ Another comment on MkT3? (rejected)
+
+data T4 =
+ -- | Comment on MkT4
+ MkT4 {}
+ -- ^ Another comment on MkT4? (rejected)
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/haddockExtraDocs.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/haddockExtraDocs.stderr
new file mode 100644
index 0000000000..b1e6cb5565
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/haddockExtraDocs.stderr
@@ -0,0 +1,16 @@
+
+haddockExtraDocs.hs:9:5: warning: [-Winvalid-haddock]
+ Multiple Haddock comments for a single entity are not allowed.
+ The extraneous comment will be ignored.
+
+haddockExtraDocs.hs:15:5: warning: [-Winvalid-haddock]
+ Multiple Haddock comments for a single entity are not allowed.
+ The extraneous comment will be ignored.
+
+haddockExtraDocs.hs:20:3: warning: [-Winvalid-haddock]
+ Multiple Haddock comments for a single entity are not allowed.
+ The extraneous comment will be ignored.
+
+haddockExtraDocs.hs:25:3: warning: [-Winvalid-haddock]
+ Multiple Haddock comments for a single entity are not allowed.
+ The extraneous comment will be ignored.
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/haddockTySyn.hs b/testsuite/tests/haddock/should_compile_flag_haddock/haddockTySyn.hs
new file mode 100644
index 0000000000..d8597e003a
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/haddockTySyn.hs
@@ -0,0 +1,3 @@
+module HaddockTySyn where
+
+type T = Int -- ^ Comment on type synonym RHS
diff --git a/testsuite/tests/haddock/should_compile_flag_haddock/haddockTySyn.stderr b/testsuite/tests/haddock/should_compile_flag_haddock/haddockTySyn.stderr
new file mode 100644
index 0000000000..cc675fe568
--- /dev/null
+++ b/testsuite/tests/haddock/should_compile_flag_haddock/haddockTySyn.stderr
@@ -0,0 +1,6 @@
+
+==================== Parser ====================
+module HaddockTySyn where
+type T = Int " Comment on type synonym RHS"
+
+
diff --git a/testsuite/tests/parser/should_compile/DumpParsedAst.stderr b/testsuite/tests/parser/should_compile/DumpParsedAst.stderr
index b14b69dc04..5a6c569ad3 100644
--- a/testsuite/tests/parser/should_compile/DumpParsedAst.stderr
+++ b/testsuite/tests/parser/should_compile/DumpParsedAst.stderr
@@ -3,6 +3,8 @@
({ DumpParsedAst.hs:1:1 }
(HsModule
+ (VirtualBraces
+ (1))
(Just
({ DumpParsedAst.hs:4:8-20 }
{ModuleName: DumpParsedAst}))
diff --git a/testsuite/tests/parser/should_compile/KindSigs.stderr b/testsuite/tests/parser/should_compile/KindSigs.stderr
index 689cc4187f..c5976593d3 100644
--- a/testsuite/tests/parser/should_compile/KindSigs.stderr
+++ b/testsuite/tests/parser/should_compile/KindSigs.stderr
@@ -3,6 +3,8 @@
({ KindSigs.hs:1:1 }
(HsModule
+ (VirtualBraces
+ (1))
(Just
({ KindSigs.hs:6:8-15 }
{ModuleName: KindSigs}))
diff --git a/testsuite/tests/parser/should_compile/T15323.stderr b/testsuite/tests/parser/should_compile/T15323.stderr
index fd48dbf203..7b8436f2cb 100644
--- a/testsuite/tests/parser/should_compile/T15323.stderr
+++ b/testsuite/tests/parser/should_compile/T15323.stderr
@@ -3,6 +3,8 @@
({ T15323.hs:1:1 }
(HsModule
+ (VirtualBraces
+ (1))
(Just
({ T15323.hs:3:8-13 }
{ModuleName: T15323}))
diff --git a/utils/check-ppr/Main.hs b/utils/check-ppr/Main.hs
index b222b726fb..9bc776d4d5 100644
--- a/utils/check-ppr/Main.hs
+++ b/utils/check-ppr/Main.hs
@@ -1,6 +1,10 @@
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.List
+import Data.Data
import GHC.Types.SrcLoc
import GHC hiding (moduleName)
import GHC.Hs.Dump
@@ -30,7 +34,8 @@ testOneFile libdir fileName = do
p <- parseOneFile libdir fileName
let
origAst = showSDoc unsafeGlobalDynFlags
- $ showAstData BlankSrcSpan (pm_parsed_source p)
+ $ showAstData BlankSrcSpan
+ $ eraseLayoutInfo (pm_parsed_source p)
pped = pragmas ++ "\n" ++ pp (pm_parsed_source p)
anns = pm_annotations p
pragmas = getPragmas anns
@@ -46,7 +51,8 @@ testOneFile libdir fileName = do
let newAstStr :: String
newAstStr = showSDoc unsafeGlobalDynFlags
- $ showAstData BlankSrcSpan (pm_parsed_source p')
+ $ showAstData BlankSrcSpan
+ $ eraseLayoutInfo (pm_parsed_source p')
writeFile newAstFile newAstStr
if origAst == newAstStr
@@ -98,4 +104,22 @@ getPragmas anns = pragmaStr
pp :: (Outputable a) => a -> String
pp a = showPpr unsafeGlobalDynFlags a
+eraseLayoutInfo :: ParsedSource -> ParsedSource
+eraseLayoutInfo = everywhere go
+ where
+ go :: forall a. Typeable a => a -> a
+ go x =
+ case eqT @a @LayoutInfo of
+ Nothing -> x
+ Just Refl -> NoLayoutInfo
+
+-- ---------------------------------------------------------------------
+-- Copied from syb for the test
+
+everywhere :: (forall a. Data a => a -> a)
+ -> (forall a. Data a => a -> a)
+everywhere f = go
+ where
+ go :: forall a. Data a => a -> a
+ go = f . gmapT go