@@ -38,7 +38,7 @@ use std::sync::{Arc, Weak};
3838use pulldown_cmark:: {
3939 BrokenLink , CodeBlockKind , CowStr , Event , LinkType , Options , Parser , Tag , TagEnd , html,
4040} ;
41- use rustc_data_structures:: fx:: FxHashMap ;
41+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap } ;
4242use rustc_errors:: { Diag , DiagMessage } ;
4343use rustc_hir:: def_id:: LocalDefId ;
4444use rustc_middle:: ty:: TyCtxt ;
@@ -1763,6 +1763,46 @@ pub(crate) fn markdown_links<'md, R>(
17631763 }
17641764 } ;
17651765
1766+ let span_for_refdef = |link : & CowStr < ' _ > , span : Range < usize > | {
1767+ // We want to underline the link's definition, but `span` will point at the entire refdef.
1768+ // Skip the label, then try to find the entire URL.
1769+ let mut square_brace_count = 0 ;
1770+ let mut iter = md. as_bytes ( ) [ span. start ..span. end ] . iter ( ) . copied ( ) . enumerate ( ) ;
1771+ for ( _i, c) in & mut iter {
1772+ match c {
1773+ b':' if square_brace_count == 0 => break ,
1774+ b'[' => square_brace_count += 1 ,
1775+ b']' => square_brace_count -= 1 ,
1776+ _ => { }
1777+ }
1778+ }
1779+ while let Some ( ( i, c) ) = iter. next ( ) {
1780+ if c == b'<' {
1781+ while let Some ( ( j, c) ) = iter. next ( ) {
1782+ match c {
1783+ b'\\' => {
1784+ let _ = iter. next ( ) ;
1785+ }
1786+ b'>' => {
1787+ return MarkdownLinkRange :: Destination (
1788+ i + 1 + span. start ..j + span. start ,
1789+ ) ;
1790+ }
1791+ _ => { }
1792+ }
1793+ }
1794+ } else if !c. is_ascii_whitespace ( ) {
1795+ while let Some ( ( j, c) ) = iter. next ( ) {
1796+ if c. is_ascii_whitespace ( ) {
1797+ return MarkdownLinkRange :: Destination ( i + span. start ..j + span. start ) ;
1798+ }
1799+ }
1800+ return MarkdownLinkRange :: Destination ( i + span. start ..span. end ) ;
1801+ }
1802+ }
1803+ span_for_link ( link, span)
1804+ } ;
1805+
17661806 let span_for_offset_backward = |span : Range < usize > , open : u8 , close : u8 | {
17671807 let mut open_brace = !0 ;
17681808 let mut close_brace = !0 ;
@@ -1844,9 +1884,16 @@ pub(crate) fn markdown_links<'md, R>(
18441884 . into_offset_iter ( ) ;
18451885 let mut links = Vec :: new ( ) ;
18461886
1887+ let mut refdefs = FxIndexMap :: default ( ) ;
1888+ for ( label, refdef) in event_iter. reference_definitions ( ) . iter ( ) {
1889+ refdefs. insert ( label. to_string ( ) , ( false , refdef. dest . to_string ( ) , refdef. span . clone ( ) ) ) ;
1890+ }
1891+
18471892 for ( event, span) in event_iter {
18481893 match event {
1849- Event :: Start ( Tag :: Link { link_type, dest_url, .. } ) if may_be_doc_link ( link_type) => {
1894+ Event :: Start ( Tag :: Link { link_type, dest_url, id, .. } )
1895+ if may_be_doc_link ( link_type) =>
1896+ {
18501897 let range = match link_type {
18511898 // Link is pulled from the link itself.
18521899 LinkType :: ReferenceUnknown | LinkType :: ShortcutUnknown => {
@@ -1856,7 +1903,12 @@ pub(crate) fn markdown_links<'md, R>(
18561903 LinkType :: Inline => span_for_offset_backward ( span, b'(' , b')' ) ,
18571904 // Link is pulled from elsewhere in the document.
18581905 LinkType :: Reference | LinkType :: Collapsed | LinkType :: Shortcut => {
1859- span_for_link ( & dest_url, span)
1906+ if let Some ( ( is_used, dest_url, span) ) = refdefs. get_mut ( & id[ ..] ) {
1907+ * is_used = true ;
1908+ span_for_refdef ( & CowStr :: from ( & dest_url[ ..] ) , span. clone ( ) )
1909+ } else {
1910+ span_for_link ( & dest_url, span)
1911+ }
18601912 }
18611913 LinkType :: Autolink | LinkType :: Email => unreachable ! ( ) ,
18621914 } ;
@@ -1873,6 +1925,18 @@ pub(crate) fn markdown_links<'md, R>(
18731925 }
18741926 }
18751927
1928+ for ( _label, ( is_used, dest_url, span) ) in refdefs. into_iter ( ) {
1929+ if !is_used
1930+ && let Some ( link) = preprocess_link ( MarkdownLink {
1931+ kind : LinkType :: Reference ,
1932+ range : span_for_refdef ( & CowStr :: from ( & dest_url[ ..] ) , span) ,
1933+ link : dest_url,
1934+ } )
1935+ {
1936+ links. push ( link) ;
1937+ }
1938+ }
1939+
18761940 links
18771941}
18781942
0 commit comments