@@ -950,6 +950,7 @@ impl LinkCollector<'_, '_> {
950950 }
951951
952952 let link = ori_link. link . replace ( "`" , "" ) ;
953+ let no_backticks_range = range_between_backticks ( & ori_link) ;
953954 let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
954955 let ( link, extra_fragment) = if parts. len ( ) > 2 {
955956 // A valid link can't have multiple #'s
@@ -973,10 +974,15 @@ impl LinkCollector<'_, '_> {
973974 } ;
974975
975976 // Parse and strip the disambiguator from the link, if present.
976- let ( mut path_str, disambiguator) = if let Ok ( ( d, path) ) = Disambiguator :: from_str ( & link) {
977- ( path. trim ( ) , Some ( d) )
978- } else {
979- ( link. trim ( ) , None )
977+ let ( mut path_str, disambiguator) = match Disambiguator :: from_str ( & link) {
978+ Ok ( Some ( ( d, path) ) ) => ( path. trim ( ) , Some ( d) ) ,
979+ Ok ( None ) => ( link. trim ( ) , None ) ,
980+ Err ( ( err_msg, relative_range) ) => {
981+ let disambiguator_range = ( no_backticks_range. start + relative_range. start )
982+ ..( no_backticks_range. start + relative_range. end ) ;
983+ disambiguator_error ( self . cx , & item, dox, disambiguator_range, & err_msg) ;
984+ return None ;
985+ }
980986 } ;
981987
982988 if path_str. contains ( |ch : char | !( ch. is_alphanumeric ( ) || ":_<>, !*&;" . contains ( ch) ) ) {
@@ -1488,6 +1494,27 @@ impl LinkCollector<'_, '_> {
14881494 }
14891495}
14901496
1497+ /// Get the section of a link between the backticks,
1498+ /// or the whole link if there aren't any backticks.
1499+ ///
1500+ /// For example:
1501+ ///
1502+ /// ```text
1503+ /// [`Foo`]
1504+ /// ^^^
1505+ /// ```
1506+ fn range_between_backticks ( ori_link : & MarkdownLink ) -> Range < usize > {
1507+ let after_first_backtick_group = ori_link. link . bytes ( ) . position ( |b| b != b'`' ) . unwrap_or ( 0 ) ;
1508+ let before_second_backtick_group = ori_link
1509+ . link
1510+ . bytes ( )
1511+ . skip ( after_first_backtick_group)
1512+ . position ( |b| b == b'`' )
1513+ . unwrap_or ( ori_link. link . len ( ) ) ;
1514+ ( ori_link. range . start + after_first_backtick_group)
1515+ ..( ori_link. range . start + before_second_backtick_group)
1516+ }
1517+
14911518#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
14921519/// Disambiguators for a link.
14931520enum Disambiguator {
@@ -1514,27 +1541,14 @@ impl Disambiguator {
15141541 }
15151542 }
15161543
1517- /// Given a link, parse and return `(disambiguator, path_str)`
1518- fn from_str ( link : & str ) -> Result < ( Self , & str ) , ( ) > {
1544+ /// Given a link, parse and return `(disambiguator, path_str)`.
1545+ ///
1546+ /// This returns `Ok(Some(...))` if a disambiguator was found,
1547+ /// `Ok(None)` if no disambiguator was found, or `Err(...)`
1548+ /// if there was a problem with the disambiguator.
1549+ fn from_str ( link : & str ) -> Result < Option < ( Self , & str ) > , ( String , Range < usize > ) > {
15191550 use Disambiguator :: { Kind , Namespace as NS , Primitive } ;
15201551
1521- let find_suffix = || {
1522- let suffixes = [
1523- ( "!()" , DefKind :: Macro ( MacroKind :: Bang ) ) ,
1524- ( "()" , DefKind :: Fn ) ,
1525- ( "!" , DefKind :: Macro ( MacroKind :: Bang ) ) ,
1526- ] ;
1527- for & ( suffix, kind) in & suffixes {
1528- if let Some ( link) = link. strip_suffix ( suffix) {
1529- // Avoid turning `!` or `()` into an empty string
1530- if !link. is_empty ( ) {
1531- return Ok ( ( Kind ( kind) , link) ) ;
1532- }
1533- }
1534- }
1535- Err ( ( ) )
1536- } ;
1537-
15381552 if let Some ( idx) = link. find ( '@' ) {
15391553 let ( prefix, rest) = link. split_at ( idx) ;
15401554 let d = match prefix {
@@ -1551,11 +1565,24 @@ impl Disambiguator {
15511565 "value" => NS ( Namespace :: ValueNS ) ,
15521566 "macro" => NS ( Namespace :: MacroNS ) ,
15531567 "prim" | "primitive" => Primitive ,
1554- _ => return find_suffix ( ) ,
1568+ _ => return Err ( ( format ! ( "unknown disambiguator `{}`" , prefix ) , 0 ..idx ) ) ,
15551569 } ;
1556- Ok ( ( d, & rest[ 1 ..] ) )
1570+ Ok ( Some ( ( d, & rest[ 1 ..] ) ) )
15571571 } else {
1558- find_suffix ( )
1572+ let suffixes = [
1573+ ( "!()" , DefKind :: Macro ( MacroKind :: Bang ) ) ,
1574+ ( "()" , DefKind :: Fn ) ,
1575+ ( "!" , DefKind :: Macro ( MacroKind :: Bang ) ) ,
1576+ ] ;
1577+ for & ( suffix, kind) in & suffixes {
1578+ if let Some ( link) = link. strip_suffix ( suffix) {
1579+ // Avoid turning `!` or `()` into an empty string
1580+ if !link. is_empty ( ) {
1581+ return Ok ( Some ( ( Kind ( kind) , link) ) ) ;
1582+ }
1583+ }
1584+ }
1585+ Ok ( None )
15591586 }
15601587 }
15611588
@@ -1979,6 +2006,17 @@ fn anchor_failure(
19792006 } ) ;
19802007}
19812008
2009+ /// Report an error in the link disambiguator.
2010+ fn disambiguator_error (
2011+ cx : & DocContext < ' _ > ,
2012+ item : & Item ,
2013+ dox : & str ,
2014+ link_range : Range < usize > ,
2015+ msg : & str ,
2016+ ) {
2017+ report_diagnostic ( cx. tcx , BROKEN_INTRA_DOC_LINKS , msg, item, dox, & link_range, |_diag, _sp| { } ) ;
2018+ }
2019+
19822020/// Report an ambiguity error, where there were multiple possible resolutions.
19832021fn ambiguity_error (
19842022 cx : & DocContext < ' _ > ,
0 commit comments