@@ -950,6 +950,7 @@ impl LinkCollector<'_, '_> {
950
950
}
951
951
952
952
let link = ori_link. link . replace ( "`" , "" ) ;
953
+ let no_backticks_range = range_between_backticks ( & ori_link) ;
953
954
let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
954
955
let ( link, extra_fragment) = if parts. len ( ) > 2 {
955
956
// A valid link can't have multiple #'s
@@ -973,10 +974,15 @@ impl LinkCollector<'_, '_> {
973
974
} ;
974
975
975
976
// 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
+ }
980
986
} ;
981
987
982
988
if path_str. contains ( |ch : char | !( ch. is_alphanumeric ( ) || ":_<>, !*&;" . contains ( ch) ) ) {
@@ -1488,6 +1494,27 @@ impl LinkCollector<'_, '_> {
1488
1494
}
1489
1495
}
1490
1496
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
+
1491
1518
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
1492
1519
/// Disambiguators for a link.
1493
1520
enum Disambiguator {
@@ -1514,27 +1541,14 @@ impl Disambiguator {
1514
1541
}
1515
1542
}
1516
1543
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 > ) > {
1519
1550
use Disambiguator :: { Kind , Namespace as NS , Primitive } ;
1520
1551
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
-
1538
1552
if let Some ( idx) = link. find ( '@' ) {
1539
1553
let ( prefix, rest) = link. split_at ( idx) ;
1540
1554
let d = match prefix {
@@ -1551,11 +1565,24 @@ impl Disambiguator {
1551
1565
"value" => NS ( Namespace :: ValueNS ) ,
1552
1566
"macro" => NS ( Namespace :: MacroNS ) ,
1553
1567
"prim" | "primitive" => Primitive ,
1554
- _ => return find_suffix ( ) ,
1568
+ _ => return Err ( ( format ! ( "unknown disambiguator `{}`" , prefix ) , 0 ..idx ) ) ,
1555
1569
} ;
1556
- Ok ( ( d, & rest[ 1 ..] ) )
1570
+ Ok ( Some ( ( d, & rest[ 1 ..] ) ) )
1557
1571
} 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 )
1559
1586
}
1560
1587
}
1561
1588
@@ -1979,6 +2006,17 @@ fn anchor_failure(
1979
2006
} ) ;
1980
2007
}
1981
2008
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
+
1982
2020
/// Report an ambiguity error, where there were multiple possible resolutions.
1983
2021
fn ambiguity_error (
1984
2022
cx : & DocContext < ' _ > ,
0 commit comments