diff --git a/doc/markup.rst b/doc/markup.rst
index 8935b7657442707f53d8c4689cfd50a8b003d968..c0b08a4067ebcb4be0c27b6668cf6ba00968c97d 100644
--- a/doc/markup.rst
+++ b/doc/markup.rst
@@ -982,6 +982,11 @@ is valid before turning it into a link but unlike with identifiers,
 whether the module is in scope isn't checked and will always be turned
 into a link.
 
+It is also possible to specify alternate text for the generated link
+using syntax analogous to that used for URLs: ::
+
+  -- | This is a reference to [the main module]("Module.Main").
+
 Itemized and Enumerated Lists
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/haddock-api/src/Haddock/Backends/Hoogle.hs b/haddock-api/src/Haddock/Backends/Hoogle.hs
index 58809f730f49c1bb13784a9ce07b393bacebf55f..9a304030cad35a75bb411ba806ab433e46f8d91e 100644
--- a/haddock-api/src/Haddock/Backends/Hoogle.hs
+++ b/haddock-api/src/Haddock/Backends/Hoogle.hs
@@ -325,7 +325,7 @@ markupTag dflags = Markup {
   markupAppend               = (++),
   markupIdentifier           = box (TagInline "a") . str . out dflags,
   markupIdentifierUnchecked  = box (TagInline "a") . str . showWrapped (out dflags . snd),
-  markupModule               = box (TagInline "a") . str,
+  markupModule               = \(ModLink m label) -> box (TagInline "a") (fromMaybe (str m) label),
   markupWarning              = box (TagInline "i"),
   markupEmphasis             = box (TagInline "i"),
   markupBold                 = box (TagInline "b"),
diff --git a/haddock-api/src/Haddock/Backends/LaTeX.hs b/haddock-api/src/Haddock/Backends/LaTeX.hs
index df81fd6e181c5d54ae2502111bd2b803a0df8908..2371695f833e9d00153237abfc3a28146dbd87fc 100644
--- a/haddock-api/src/Haddock/Backends/LaTeX.hs
+++ b/haddock-api/src/Haddock/Backends/LaTeX.hs
@@ -1210,7 +1210,12 @@ latexMarkup = Markup
   , markupAppend               = \l r v -> l v . r v
   , markupIdentifier           = \i v -> inlineElem (markupId v (fmap occName i))
   , markupIdentifierUnchecked  = \i v -> inlineElem (markupId v (fmap snd i))
-  , markupModule               = \m _ -> inlineElem (let (mdl,_ref) = break (=='#') m in (tt (text mdl)))
+  , markupModule               =
+      \(ModLink m mLabel) v ->
+        case mLabel of
+          Just lbl -> inlineElem . tt $ lbl v empty
+          Nothing -> inlineElem (let (mdl,_ref) = break (=='#') m
+                                 in (tt (text mdl)))
   , markupWarning              = \p v -> p v
   , markupEmphasis             = \p v -> inlineElem (emph (p v empty))
   , markupBold                 = \p v -> inlineElem (bold (p v empty))
diff --git a/haddock-api/src/Haddock/Backends/Xhtml/DocMarkup.hs b/haddock-api/src/Haddock/Backends/Xhtml/DocMarkup.hs
index 378d0559dfbab52f73bc636cee03115db87422b7..7670b1939a9ada5bec0f20e731d4ba43248db796 100644
--- a/haddock-api/src/Haddock/Backends/Xhtml/DocMarkup.hs
+++ b/haddock-api/src/Haddock/Backends/Xhtml/DocMarkup.hs
@@ -44,13 +44,14 @@ parHtmlMarkup qual insertAnchors ppId = Markup {
   markupAppend               = (+++),
   markupIdentifier           = thecode . ppId insertAnchors,
   markupIdentifierUnchecked  = thecode . ppUncheckedLink qual,
-  markupModule               = \m -> let (mdl,ref) = break (=='#') m
-                                         -- Accomodate for old style
-                                         -- foo\#bar anchors
-                                         mdl' = case reverse mdl of
-                                           '\\':_ -> init mdl
-                                           _ -> mdl
-                                     in ppModuleRef (mkModuleName mdl') ref,
+  markupModule               = \(ModLink m lbl) ->
+                                 let (mdl,ref) = break (=='#') m
+                                       -- Accomodate for old style
+                                       -- foo\#bar anchors
+                                     mdl' = case reverse mdl of
+                                              '\\':_ -> init mdl
+                                              _ -> mdl
+                                 in ppModuleRef lbl (mkModuleName mdl') ref,
   markupWarning              = thediv ! [theclass "warning"],
   markupEmphasis             = emphasize,
   markupBold                 = strong,
diff --git a/haddock-api/src/Haddock/Backends/Xhtml/Names.hs b/haddock-api/src/Haddock/Backends/Xhtml/Names.hs
index 8553cdfb94ab233d303deb7bde9b286531d1ce8a..b324fa38366ca863a8f6e891f9d6d5ab43c635d9 100644
--- a/haddock-api/src/Haddock/Backends/Xhtml/Names.hs
+++ b/haddock-api/src/Haddock/Backends/Xhtml/Names.hs
@@ -186,9 +186,12 @@ ppModule mdl = anchor ! [href (moduleUrl mdl)]
                << toHtml (moduleString mdl)
 
 
-ppModuleRef :: ModuleName -> String -> Html
-ppModuleRef mdl ref = anchor ! [href (moduleHtmlFile' mdl ++ ref)]
-                      << toHtml (moduleNameString mdl)
+ppModuleRef :: Maybe Html -> ModuleName -> String -> Html
+ppModuleRef Nothing mdl ref = anchor ! [href (moduleHtmlFile' mdl ++ ref)]
+                              << toHtml (moduleNameString mdl)
+ppModuleRef (Just lbl) mdl ref = anchor ! [href (moduleHtmlFile' mdl ++ ref)]
+                                 << lbl
+
     -- NB: The ref parameter already includes the '#'.
     -- This function is only called from markupModule expanding a
     -- DocModule, which doesn't seem to be ever be used.
diff --git a/haddock-api/src/Haddock/Interface/Json.hs b/haddock-api/src/Haddock/Interface/Json.hs
index 4e27160276213704ecd51dc25535757e4e484eb7..95889a634b2dcfb8317cdfb3418a334887592242 100644
--- a/haddock-api/src/Haddock/Interface/Json.hs
+++ b/haddock-api/src/Haddock/Interface/Json.hs
@@ -98,9 +98,9 @@ jsonDoc (DocIdentifierUnchecked modName) = jsonObject
     , ("modName", jsonString (showModName modName))
     ]
 
-jsonDoc (DocModule s) = jsonObject
+jsonDoc (DocModule (ModLink m _l)) = jsonObject
     [ ("tag", jsonString "DocModule")
-    , ("string", jsonString s)
+    , ("string", jsonString m)
     ]
 
 jsonDoc (DocWarning x) = jsonObject
diff --git a/haddock-api/src/Haddock/Interface/LexParseRn.hs b/haddock-api/src/Haddock/Interface/LexParseRn.hs
index d1d6bb317713eb75f5b1c6d1472dc0654389cdf9..87210273c8335f403793ed8e84f5448277bcc55a 100644
--- a/haddock-api/src/Haddock/Interface/LexParseRn.hs
+++ b/haddock-api/src/Haddock/Interface/LexParseRn.hs
@@ -148,7 +148,7 @@ rename dflags gre = rn
       DocDefList list -> DocDefList <$> traverse (\(a, b) -> (,) <$> rn a <*> rn b) list
       DocCodeBlock doc -> DocCodeBlock <$> rn doc
       DocIdentifierUnchecked x -> pure (DocIdentifierUnchecked x)
-      DocModule str -> pure (DocModule str)
+      DocModule (ModLink m l) -> DocModule . ModLink m <$> traverse rn l
       DocHyperlink (Hyperlink u l) -> DocHyperlink . Hyperlink u <$> traverse rn l
       DocPic str -> pure (DocPic str)
       DocMathInline str -> pure (DocMathInline str)
diff --git a/haddock-api/src/Haddock/InterfaceFile.hs b/haddock-api/src/Haddock/InterfaceFile.hs
index 35f21f6c7ce4d5e008ad9fefef5da39c06351f90..966901dfada7af54faa7bef3467610a0640a88d6 100644
--- a/haddock-api/src/Haddock/InterfaceFile.hs
+++ b/haddock-api/src/Haddock/InterfaceFile.hs
@@ -46,6 +46,8 @@ import GHC.Types.Unique.FM
 import GHC.Types.Unique.Supply
 import GHC.Types.Unique
 
+import Documentation.Haddock.Parser (parseModLink)
+
 
 data InterfaceFile = InterfaceFile {
   ifLinkEnv         :: LinkEnv,
@@ -69,6 +71,18 @@ ifUnitId if_ =
 binaryInterfaceMagic :: Word32
 binaryInterfaceMagic = 0xD0Cface
 
+-- Note [The DocModule story]
+--
+-- Breaking changes to the DocH type result in Haddock being unable to read
+-- existing interfaces. This is especially painful for interfaces shipped
+-- with GHC distributions since there is no easy way to regenerate them!
+--
+-- PR #1315 introduced a breaking change to the DocModule constructor. To
+-- maintain backward compatibility we
+--
+-- Parse the old DocModule constructor format (tag 5) and parse the contained
+-- string into a proper ModLink structure. When writing interfaces we exclusively
+-- use the new DocModule format (tag 24)
 
 -- IMPORTANT: Since datatypes in the GHC API might change between major
 -- versions, and because we store GHC datatypes in our interface files, we need
@@ -84,10 +98,10 @@ binaryInterfaceMagic = 0xD0Cface
 --
 binaryInterfaceVersion :: Word16
 #if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,1,0)
-binaryInterfaceVersion = 37
+binaryInterfaceVersion = 38
 
 binaryInterfaceVersionCompatibility :: [Word16]
-binaryInterfaceVersionCompatibility = [binaryInterfaceVersion]
+binaryInterfaceVersionCompatibility = [37, binaryInterfaceVersion]
 #else
 #error Unsupported GHC version
 #endif
@@ -444,6 +458,15 @@ instance Binary a => Binary (Hyperlink a) where
         label <- get bh
         return (Hyperlink url label)
 
+instance Binary a => Binary (ModLink a) where
+    put_ bh (ModLink m label) = do
+        put_ bh m
+        put_ bh label
+    get bh = do
+        m <- get bh
+        label <- get bh
+        return (ModLink m label)
+
 instance Binary Picture where
     put_ bh (Picture uri title) = do
         put_ bh uri
@@ -522,9 +545,6 @@ instance (Binary mod, Binary id) => Binary (DocH mod id) where
     put_ bh (DocIdentifier ae) = do
             putByte bh 4
             put_ bh ae
-    put_ bh (DocModule af) = do
-            putByte bh 5
-            put_ bh af
     put_ bh (DocEmphasis ag) = do
             putByte bh 6
             put_ bh ag
@@ -579,6 +599,10 @@ instance (Binary mod, Binary id) => Binary (DocH mod id) where
     put_ bh (DocTable x) = do
             putByte bh 23
             put_ bh x
+    -- See note [The DocModule story]
+    put_ bh (DocModule af) = do
+            putByte bh 24
+            put_ bh af
 
     get bh = do
             h <- getByte bh
@@ -598,9 +622,10 @@ instance (Binary mod, Binary id) => Binary (DocH mod id) where
               4 -> do
                     ae <- get bh
                     return (DocIdentifier ae)
+              -- See note [The DocModule story]
               5 -> do
                     af <- get bh
-                    return (DocModule af)
+                    return (parseModLink af)
               6 -> do
                     ag <- get bh
                     return (DocEmphasis ag)
@@ -655,6 +680,10 @@ instance (Binary mod, Binary id) => Binary (DocH mod id) where
               23 -> do
                     x <- get bh
                     return (DocTable x)
+              -- See note [The DocModule story]
+              24 -> do
+                    af <- get bh
+                    return (DocModule af)
               _ -> error "invalid binary data found in the interface file"
 
 
diff --git a/haddock-api/src/Haddock/Types.hs b/haddock-api/src/Haddock/Types.hs
index aa76f8f6b3792446927a9e8f546b71392d91e52c..53d01565d48eaaf6af5ef8d6c2d0219eb6e3f46e 100644
--- a/haddock-api/src/Haddock/Types.hs
+++ b/haddock-api/src/Haddock/Types.hs
@@ -501,6 +501,9 @@ instance NFData id => NFData (Header id) where
 instance NFData id => NFData (Hyperlink id) where
   rnf (Hyperlink a b) = a `deepseq` b `deepseq` ()
 
+instance NFData id => NFData (ModLink id) where
+  rnf (ModLink a b) = a `deepseq` b `deepseq` ()
+
 instance NFData Picture where
   rnf (Picture a b) = a `deepseq` b `deepseq` ()
 
diff --git a/haddock-library/fixtures/Fixtures.hs b/haddock-library/fixtures/Fixtures.hs
index 72ea85253a5b2463265e462722f5ad59bf3ef963..101bce65d968225783ce4c66d8802e85150ebdc0 100644
--- a/haddock-library/fixtures/Fixtures.hs
+++ b/haddock-library/fixtures/Fixtures.hs
@@ -149,6 +149,9 @@ instance ToExpr id => ToExpr (Header id)
 deriving instance Generic (Hyperlink id)
 instance ToExpr id => ToExpr (Hyperlink id)
 
+deriving instance Generic (ModLink id)
+instance ToExpr id => ToExpr (ModLink id)
+
 deriving instance Generic Picture
 instance ToExpr Picture
 
diff --git a/haddock-library/src/Documentation/Haddock/Markup.hs b/haddock-library/src/Documentation/Haddock/Markup.hs
index 365041eed92f81ea77cdb38db1a6fd83f0d205f8..0919737f442838f9c02add5b97f4f546dfcf3b98 100644
--- a/haddock-library/src/Documentation/Haddock/Markup.hs
+++ b/haddock-library/src/Documentation/Haddock/Markup.hs
@@ -16,7 +16,7 @@ markup m (DocString s)                  = markupString m s
 markup m (DocParagraph d)               = markupParagraph m (markup m d)
 markup m (DocIdentifier x)              = markupIdentifier m x
 markup m (DocIdentifierUnchecked x)     = markupIdentifierUnchecked m x
-markup m (DocModule mod0)               = markupModule m mod0
+markup m (DocModule (ModLink mo l))     = markupModule m (ModLink mo (fmap (markup m) l))
 markup m (DocWarning d)                 = markupWarning m (markup m d)
 markup m (DocEmphasis d)                = markupEmphasis m (markup m d)
 markup m (DocBold d)                    = markupBold m (markup m d)
@@ -78,7 +78,7 @@ plainMarkup plainMod plainIdent = Markup {
   markupAppend               = (++),
   markupIdentifier           = plainIdent,
   markupIdentifierUnchecked  = plainMod,
-  markupModule               = id,
+  markupModule               = \(ModLink m lbl) -> fromMaybe m lbl,
   markupWarning              = id,
   markupEmphasis             = id,
   markupBold                 = id,
diff --git a/haddock-library/src/Documentation/Haddock/Parser.hs b/haddock-library/src/Documentation/Haddock/Parser.hs
index a3bba38ac788c7632c552afe1e31d1a7093c0b33..bb8745a50c038d19d002645970c19f84370643b3 100644
--- a/haddock-library/src/Documentation/Haddock/Parser.hs
+++ b/haddock-library/src/Documentation/Haddock/Parser.hs
@@ -18,6 +18,7 @@
 module Documentation.Haddock.Parser (
   parseString,
   parseParas,
+  parseModLink,
   overIdentifier,
   toRegular,
   Identifier
@@ -72,7 +73,7 @@ overIdentifier f d = g d
     g (DocString x) = DocString x
     g (DocParagraph x) = DocParagraph $ g x
     g (DocIdentifierUnchecked x) = DocIdentifierUnchecked x
-    g (DocModule x) = DocModule x
+    g (DocModule (ModLink m x)) = DocModule (ModLink m (fmap g x))
     g (DocWarning x) = DocWarning $ g x
     g (DocEmphasis x) = DocEmphasis $ g x
     g (DocMonospaced x) = DocMonospaced $ g x
@@ -136,6 +137,9 @@ parseString = parseText . T.pack
 parseText :: Text -> DocH mod Identifier
 parseText = parseParagraph . T.dropWhile isSpace . T.filter (/= '\r')
 
+parseModLink :: String -> DocH mod id
+parseModLink s = snd $ parse moduleName (T.pack s)
+
 parseParagraph :: Text -> DocH mod Identifier
 parseParagraph = snd . parse p
   where
@@ -148,6 +152,7 @@ parseParagraph = snd . parse p
                                     , mathDisplay
                                     , mathInline
                                     , markdownImage
+                                    , markdownLink
                                     , hyperlink
                                     , bold
                                     , emphasis
@@ -242,7 +247,12 @@ monospace = DocMonospaced . parseParagraph
 -- Note that we allow '#' and '\' to support anchors (old style anchors are of
 -- the form "SomeModule\#anchor").
 moduleName :: Parser (DocH mod a)
-moduleName = DocModule <$> ("\"" *> (modid `maybeFollowedBy` anchor_) <* "\"")
+moduleName = DocModule . flip ModLink Nothing <$> ("\"" *> moduleNameString <* "\"")
+
+-- | A module name, optionally with an anchor
+--
+moduleNameString :: Parser String
+moduleNameString = modid `maybeFollowedBy` anchor_
   where
     modid = intercalate "." <$> conid `Parsec.sepBy1` "."
     anchor_ = (++)
@@ -250,13 +260,30 @@ moduleName = DocModule <$> ("\"" *> (modid `maybeFollowedBy` anchor_) <* "\"")
       <*> many (Parsec.satisfy (\c -> c /= '"' && not (isSpace c)))
 
     maybeFollowedBy pre suf = (\x -> maybe x (x ++)) <$> pre <*> optional suf
-
+    conid :: Parser String
     conid = (:)
       <$> Parsec.satisfy (\c -> isAlpha c && isUpper c)
       <*> many conChar
 
     conChar = Parsec.alphaNum <|> Parsec.char '_'
 
+-- | A labeled link to an indentifier, module or url using markdown
+-- syntax.
+markdownLink :: Parser (DocH mod Identifier)
+markdownLink = do
+  lbl <- markdownLinkText
+  choice' [ markdownModuleName lbl, markdownURL lbl ]
+  where
+    markdownModuleName lbl = do
+      mn <- "(" *> skipHorizontalSpace *>
+            "\"" *> moduleNameString <* "\""
+            <* skipHorizontalSpace <* ")"
+      pure $ DocModule (ModLink mn (Just lbl))
+
+    markdownURL lbl = do
+      target <- markdownLinkTarget
+      pure $ DocHyperlink $ Hyperlink target (Just lbl)
+
 -- | Picture parser, surrounded by \<\< and \>\>. It's possible to specify
 -- a title for the picture.
 --
@@ -290,9 +317,11 @@ mathDisplay = DocMathDisplay . T.unpack
 -- >>> parseString "![some /emphasis/ in a description](www.site.com)"
 -- DocPic (Picture "www.site.com" (Just "some emphasis in a description"))
 markdownImage :: Parser (DocH mod Identifier)
-markdownImage = DocPic . fromHyperlink <$> ("!" *> linkParser)
+markdownImage = do
+  text <- markup stringMarkup <$> ("!" *> markdownLinkText)
+  url <- markdownLinkTarget
+  pure $ DocPic (Picture url (Just text))
   where
-    fromHyperlink (Hyperlink u l) = Picture u (fmap (markup stringMarkup) l)
     stringMarkup = plainMarkup (const "") renderIdent
     renderIdent (Identifier ns l c r) = renderNs ns <> [l] <> c <> [r]
 
@@ -772,22 +801,21 @@ codeblock =
           | otherwise = Just $ c == '\n'
 
 hyperlink :: Parser (DocH mod Identifier)
-hyperlink = choice' [ angleBracketLink, markdownLink, autoUrl ]
+hyperlink = choice' [ angleBracketLink, autoUrl ]
 
 angleBracketLink :: Parser (DocH mod a)
 angleBracketLink =
     DocHyperlink . makeLabeled (\s -> Hyperlink s . fmap DocString)
     <$> disallowNewline ("<" *> takeUntil ">")
 
-markdownLink :: Parser (DocH mod Identifier)
-markdownLink = DocHyperlink <$> linkParser
+-- | The text for a markdown link, enclosed in square brackets.
+markdownLinkText :: Parser (DocH mod Identifier)
+markdownLinkText = parseParagraph . T.strip <$> ("[" *> takeUntil "]")
 
-linkParser :: Parser (Hyperlink (DocH mod Identifier))
-linkParser = flip Hyperlink <$> label <*> (whitespace *> url)
+-- | The target for a markdown link, enclosed in parenthesis.
+markdownLinkTarget :: Parser String
+markdownLinkTarget = whitespace *> url
   where
-    label :: Parser (Maybe (DocH mod Identifier))
-    label = Just . parseParagraph . T.strip <$> ("[" *> takeUntil "]")
-
     whitespace :: Parser ()
     whitespace = skipHorizontalSpace <* optional ("\n" *> skipHorizontalSpace)
 
diff --git a/haddock-library/src/Documentation/Haddock/Types.hs b/haddock-library/src/Documentation/Haddock/Types.hs
index 12ccd28dbd7752c1146c02a99261d049a5a8644a..252eb425c5587167c96d692cb412f3de4e4c4f32 100644
--- a/haddock-library/src/Documentation/Haddock/Types.hs
+++ b/haddock-library/src/Documentation/Haddock/Types.hs
@@ -73,6 +73,11 @@ data Hyperlink id = Hyperlink
   , hyperlinkLabel :: Maybe id
   } deriving (Eq, Show, Functor, Foldable, Traversable)
 
+data ModLink id = ModLink
+  { modLinkName   :: String
+  , modLinkLabel :: Maybe id
+  } deriving (Eq, Show, Functor, Foldable, Traversable)
+
 data Picture = Picture
   { pictureUri   :: String
   , pictureTitle :: Maybe String
@@ -111,7 +116,8 @@ data DocH mod id
   | DocIdentifier id
   | DocIdentifierUnchecked mod
   -- ^ A qualified identifier that couldn't be resolved.
-  | DocModule String
+  | DocModule (ModLink (DocH mod id))
+  -- ^ A link to a module, with an optional label.
   | DocWarning (DocH mod id)
   -- ^ This constructor has no counterpart in Haddock markup.
   | DocEmphasis (DocH mod id)
@@ -142,7 +148,7 @@ instance Bifunctor DocH where
   bimap f g (DocParagraph doc) = DocParagraph (bimap f g doc)
   bimap _ g (DocIdentifier i) = DocIdentifier (g i)
   bimap f _ (DocIdentifierUnchecked m) = DocIdentifierUnchecked (f m)
-  bimap _ _ (DocModule s) = DocModule s
+  bimap f g (DocModule (ModLink m lbl)) = DocModule (ModLink m (fmap (bimap f g) lbl))
   bimap f g (DocWarning doc) = DocWarning (bimap f g doc)
   bimap f g (DocEmphasis doc) = DocEmphasis (bimap f g doc)
   bimap f g (DocMonospaced doc) = DocMonospaced (bimap f g doc)
@@ -189,7 +195,7 @@ instance Bitraversable DocH where
   bitraverse f g (DocParagraph doc) = DocParagraph <$> bitraverse f g doc
   bitraverse _ g (DocIdentifier i) = DocIdentifier <$> g i
   bitraverse f _ (DocIdentifierUnchecked m) = DocIdentifierUnchecked <$> f m
-  bitraverse _ _ (DocModule s) = pure (DocModule s)
+  bitraverse f g (DocModule (ModLink m lbl)) = DocModule <$> (ModLink m <$> traverse (bitraverse f g) lbl)
   bitraverse f g (DocWarning doc) = DocWarning <$> bitraverse f g doc
   bitraverse f g (DocEmphasis doc) = DocEmphasis <$> bitraverse f g doc
   bitraverse f g (DocMonospaced doc) = DocMonospaced <$> bitraverse f g doc
@@ -234,7 +240,7 @@ data DocMarkupH mod id a = Markup
   , markupAppend               :: a -> a -> a
   , markupIdentifier           :: id -> a
   , markupIdentifierUnchecked  :: mod -> a
-  , markupModule               :: String -> a
+  , markupModule               :: ModLink a -> a
   , markupWarning              :: a -> a
   , markupEmphasis             :: a -> a
   , markupBold                 :: a -> a
diff --git a/haddock-library/test/Documentation/Haddock/ParserSpec.hs b/haddock-library/test/Documentation/Haddock/ParserSpec.hs
index 1724c66448a6ab5b0322cbbe2765d1f859feb54f..5fa73ecd5ced7209d66b06811813cafbec18f155 100644
--- a/haddock-library/test/Documentation/Haddock/ParserSpec.hs
+++ b/haddock-library/test/Documentation/Haddock/ParserSpec.hs
@@ -403,20 +403,20 @@ spec = do
     context "when parsing module strings" $ do
       it "should parse a module on its own" $ do
         "\"Module\"" `shouldParseTo`
-          DocModule "Module"
+          DocModule (ModLink "Module" Nothing)
 
       it "should parse a module inline" $ do
         "This is a \"Module\"." `shouldParseTo`
-          "This is a " <> DocModule "Module" <> "."
+          "This is a " <> DocModule (ModLink "Module" Nothing) <> "."
 
       it "can accept a simple module name" $ do
-        "\"Hello\"" `shouldParseTo` DocModule "Hello"
+        "\"Hello\"" `shouldParseTo` DocModule (ModLink "Hello" Nothing)
 
       it "can accept a module name with dots" $ do
-        "\"Hello.World\"" `shouldParseTo` DocModule "Hello.World"
+        "\"Hello.World\"" `shouldParseTo` DocModule (ModLink "Hello.World" Nothing)
 
       it "can accept a module name with unicode" $ do
-        "\"Hello.Worldλ\"" `shouldParseTo` DocModule "Hello.Worldλ"
+        "\"Hello.Worldλ\"" `shouldParseTo` DocModule (ModLink "Hello.Worldλ" Nothing)
 
       it "parses a module name with a trailing dot as regular quoted string" $ do
         "\"Hello.\"" `shouldParseTo` "\"Hello.\""
@@ -428,19 +428,85 @@ spec = do
         "\"Hello&[{}(=*)+]!\"" `shouldParseTo` "\"Hello&[{}(=*)+]!\""
 
       it "accepts a module name with unicode" $ do
-        "\"Foo.Barλ\"" `shouldParseTo` DocModule "Foo.Barλ"
+        "\"Foo.Barλ\"" `shouldParseTo` DocModule (ModLink "Foo.Barλ" Nothing)
 
       it "treats empty module name as regular double quotes" $ do
         "\"\"" `shouldParseTo` "\"\""
 
       it "accepts anchor reference syntax as DocModule" $ do
-        "\"Foo#bar\"" `shouldParseTo` DocModule "Foo#bar"
+        "\"Foo#bar\"" `shouldParseTo` DocModule (ModLink "Foo#bar" Nothing)
 
       it "accepts anchor with hyphen as DocModule" $ do
-        "\"Foo#bar-baz\"" `shouldParseTo` DocModule "Foo#bar-baz"
+        "\"Foo#bar-baz\"" `shouldParseTo` DocModule (ModLink "Foo#bar-baz" Nothing)
 
       it "accepts old anchor reference syntax as DocModule" $ do
-        "\"Foo\\#bar\"" `shouldParseTo` DocModule "Foo\\#bar"
+        "\"Foo\\#bar\"" `shouldParseTo` DocModule (ModLink "Foo\\#bar" Nothing)
+
+    context "when parsing labeled module links" $ do
+      it "parses a simple labeled module link" $ do
+        "[some label](\"Some.Module\")" `shouldParseTo`
+          DocModule (ModLink "Some.Module" (Just "some label"))
+
+      it "allows escaping in label" $ do
+        "[some\\] label](\"Some.Module\")" `shouldParseTo`
+          DocModule (ModLink "Some.Module" (Just "some] label"))
+
+      it "strips leading and trailing whitespace from label" $ do
+        "[  some label  ](\"Some.Module\")" `shouldParseTo`
+          DocModule (ModLink "Some.Module" (Just "some label"))
+
+      it "allows whitespace in module name link" $ do
+        "[some label]( \"Some.Module\"\t )" `shouldParseTo`
+          DocModule (ModLink "Some.Module" (Just "some label"))
+
+      it "allows inline markup in the label" $ do
+        "[something /emphasized/](\"Some.Module\")" `shouldParseTo`
+          DocModule (ModLink "Some.Module" (Just ("something " <> DocEmphasis "emphasized")))
+
+      it "should parse a labeled module on its own" $ do
+        "[label](\"Module\")" `shouldParseTo`
+          DocModule (ModLink "Module" (Just "label"))
+
+      it "should parse a labeled module inline" $ do
+        "This is a [label](\"Module\")." `shouldParseTo`
+          "This is a " <> DocModule (ModLink "Module" (Just "label")) <> "."
+
+      it "can accept a labeled module name with dots" $ do
+        "[label](\"Hello.World\")" `shouldParseTo` DocModule (ModLink "Hello.World" (Just "label"))
+
+      it "can accept a labeled module name with unicode" $ do
+        "[label](\"Hello.Worldλ\")" `shouldParseTo` DocModule (ModLink "Hello.Worldλ" (Just "label"))
+
+      it "parses a labeled module name with a trailing dot as a hyperlink" $ do
+        "[label](\"Hello.\")" `shouldParseTo`
+          hyperlink "\"Hello.\"" (Just "label")
+
+      it "parses a labeled module name with a space as a regular string" $ do
+        "[label](\"Hello World\")" `shouldParseTo` "[label](\"Hello World\")"
+
+      it "parses a module name with invalid characters as a hyperlink" $ do
+        "[label](\"Hello&[{}(=*+]!\")" `shouldParseTo`
+          hyperlink "\"Hello&[{}(=*+]!\"" (Just "label")
+
+      it "accepts a labeled module name with unicode" $ do
+        "[label](\"Foo.Barλ\")" `shouldParseTo`
+          DocModule (ModLink "Foo.Barλ" (Just "label"))
+
+      it "treats empty labeled module name as empty hyperlink" $ do
+        "[label](\"\")" `shouldParseTo`
+          hyperlink "\"\"" (Just "label")
+
+      it "accepts anchor reference syntax for labeled module name" $ do
+        "[label](\"Foo#bar\")" `shouldParseTo`
+          DocModule (ModLink "Foo#bar" (Just "label"))
+
+      it "accepts old anchor reference syntax for labeled module name" $ do
+        "[label](\"Foo\\#bar\")" `shouldParseTo`
+          DocModule (ModLink "Foo\\#bar" (Just "label"))
+
+      it "interprets empty label as a unlabeled module name" $ do
+        "[](\"Module.Name\")" `shouldParseTo`
+          "[](" <> DocModule (ModLink "Module.Name" Nothing) <> ")"
 
   describe "parseParas" $ do
     let infix 1 `shouldParseTo`