@@ -17,6 +17,7 @@ use rustc_span::symbol::Ident;
17
17
use rustc_span:: symbol:: Symbol ;
18
18
use rustc_span:: DUMMY_SP ;
19
19
20
+ use std:: cell:: Cell ;
20
21
use std:: ops:: Range ;
21
22
22
23
use crate :: clean:: * ;
@@ -62,11 +63,15 @@ struct LinkCollector<'a, 'tcx> {
62
63
cx : & ' a DocContext < ' tcx > ,
63
64
// NOTE: this may not necessarily be a module in the current crate
64
65
mod_ids : Vec < DefId > ,
66
+ /// This is used to store the kind of associated items,
67
+ /// because `clean` and the disambiguator code expect them to be different.
68
+ /// See the code for associated items on inherent impls for details.
69
+ kind_side_channel : Cell < Option < DefKind > > ,
65
70
}
66
71
67
72
impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
68
73
fn new ( cx : & ' a DocContext < ' tcx > ) -> Self {
69
- LinkCollector { cx, mod_ids : Vec :: new ( ) }
74
+ LinkCollector { cx, mod_ids : Vec :: new ( ) , kind_side_channel : Cell :: new ( None ) }
70
75
}
71
76
72
77
fn variant_field (
@@ -174,7 +179,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
174
179
fn resolve (
175
180
& self ,
176
181
path_str : & str ,
177
- disambiguator : Option < & str > ,
182
+ disambiguator : Option < Disambiguator > ,
178
183
ns : Namespace ,
179
184
current_item : & Option < String > ,
180
185
parent_id : Option < DefId > ,
@@ -214,7 +219,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
214
219
Res :: Def ( DefKind :: Mod , _) => {
215
220
// This resolved to a module, but if we were passed `type@`,
216
221
// we want primitive types to take precedence instead.
217
- if disambiguator == Some ( "type" ) {
222
+ if disambiguator == Some ( Disambiguator :: Namespace ( Namespace :: TypeNS ) ) {
218
223
if let Some ( prim) = is_primitive ( path_str, ns) {
219
224
if extra_fragment. is_some ( ) {
220
225
return Err ( ErrorKind :: AnchorFailure ( AnchorFailure :: Primitive ) ) ;
@@ -347,6 +352,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
347
352
AnchorFailure :: AssocConstant
348
353
} ) )
349
354
} else {
355
+ // HACK(jynelson): `clean` expects the type, not the associated item.
356
+ // but the disambiguator logic expects the associated item.
357
+ // Store the kind in a side channel so that only the disambiguator logic looks at it.
358
+ self . kind_side_channel . replace ( Some ( item. kind . as_def_kind ( ) ) ) ;
350
359
Ok ( ( ty_res, Some ( format ! ( "{}.{}" , out, item_name) ) ) )
351
360
}
352
361
} else {
@@ -415,7 +424,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
415
424
AnchorFailure :: Method
416
425
} ) )
417
426
} else {
418
- Ok ( ( ty_res, Some ( format ! ( "{}.{}" , kind, item_name) ) ) )
427
+ let res = Res :: Def ( item. kind . as_def_kind ( ) , item. def_id ) ;
428
+ Ok ( ( res, Some ( format ! ( "{}.{}" , kind, item_name) ) ) )
419
429
}
420
430
} else {
421
431
self . variant_field ( path_str, current_item, module_id)
@@ -574,46 +584,14 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
574
584
} ;
575
585
let resolved_self;
576
586
let mut path_str;
587
+ let disambiguator;
577
588
let ( res, fragment) = {
578
- let mut kind = None ;
579
- let mut disambiguator = None ;
580
- path_str = if let Some ( prefix) =
581
- [ "struct@" , "enum@" , "type@" , "trait@" , "union@" , "module@" , "mod@" ]
582
- . iter ( )
583
- . find ( |p| link. starts_with ( * * p) )
584
- {
585
- kind = Some ( TypeNS ) ;
586
- disambiguator = Some ( & prefix[ ..prefix. len ( ) - 1 ] ) ;
587
- link. trim_start_matches ( prefix)
588
- } else if let Some ( prefix) =
589
- [ "const@" , "static@" , "value@" , "function@" , "fn@" , "method@" ]
590
- . iter ( )
591
- . find ( |p| link. starts_with ( * * p) )
592
- {
593
- kind = Some ( ValueNS ) ;
594
- disambiguator = Some ( & prefix[ ..prefix. len ( ) - 1 ] ) ;
595
- link. trim_start_matches ( prefix)
596
- } else if link. ends_with ( "!()" ) {
597
- kind = Some ( MacroNS ) ;
598
- link. trim_end_matches ( "!()" )
599
- } else if link. ends_with ( "()" ) {
600
- kind = Some ( ValueNS ) ;
601
- disambiguator = Some ( "fn" ) ;
602
- link. trim_end_matches ( "()" )
603
- } else if link. starts_with ( "macro@" ) {
604
- kind = Some ( MacroNS ) ;
605
- disambiguator = Some ( "macro" ) ;
606
- link. trim_start_matches ( "macro@" )
607
- } else if link. starts_with ( "derive@" ) {
608
- kind = Some ( MacroNS ) ;
609
- disambiguator = Some ( "derive" ) ;
610
- link. trim_start_matches ( "derive@" )
611
- } else if link. ends_with ( '!' ) {
612
- kind = Some ( MacroNS ) ;
613
- disambiguator = Some ( "macro" ) ;
614
- link. trim_end_matches ( '!' )
589
+ path_str = if let Ok ( ( d, path) ) = Disambiguator :: from_str ( & link) {
590
+ disambiguator = Some ( d) ;
591
+ path
615
592
} else {
616
- & link[ ..]
593
+ disambiguator = None ;
594
+ & link
617
595
}
618
596
. trim ( ) ;
619
597
@@ -646,7 +624,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
646
624
}
647
625
}
648
626
649
- match kind {
627
+ match disambiguator . map ( Disambiguator :: ns ) {
650
628
Some ( ns @ ValueNS ) => {
651
629
match self . resolve (
652
630
path_str,
@@ -789,6 +767,42 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
789
767
} else {
790
768
debug ! ( "intra-doc link to {} resolved to {:?}" , path_str, res) ;
791
769
770
+ // Disallow e.g. linking to enums with `struct@`
771
+ if let Res :: Def ( kind, id) = res {
772
+ debug ! ( "saw kind {:?} with disambiguator {:?}" , kind, disambiguator) ;
773
+ match ( self . kind_side_channel . take ( ) . unwrap_or ( kind) , disambiguator) {
774
+ | ( DefKind :: Const | DefKind :: ConstParam | DefKind :: AssocConst | DefKind :: AnonConst , Some ( Disambiguator :: Kind ( DefKind :: Const ) ) )
775
+ // NOTE: this allows 'method' to mean both normal functions and associated functions
776
+ // This can't cause ambiguity because both are in the same namespace.
777
+ | ( DefKind :: Fn | DefKind :: AssocFn , Some ( Disambiguator :: Kind ( DefKind :: Fn ) ) )
778
+ // These are namespaces; allow anything in the namespace to match
779
+ | ( _, Some ( Disambiguator :: Namespace ( _) ) )
780
+ // If no disambiguator given, allow anything
781
+ | ( _, None )
782
+ // All of these are valid, so do nothing
783
+ => { }
784
+ ( actual, Some ( Disambiguator :: Kind ( expected) ) ) if actual == expected => { }
785
+ ( _, Some ( Disambiguator :: Kind ( expected) ) ) => {
786
+ // The resolved item did not match the disambiguator; give a better error than 'not found'
787
+ let msg = format ! ( "incompatible link kind for `{}`" , path_str) ;
788
+ report_diagnostic ( cx, & msg, & item, & dox, link_range, |diag, sp| {
789
+ // HACK(jynelson): by looking at the source I saw the DefId we pass
790
+ // for `expected.descr()` doesn't matter, since it's not a crate
791
+ let note = format ! ( "this link resolved to {} {}, which is not {} {}" , kind. article( ) , kind. descr( id) , expected. article( ) , expected. descr( id) ) ;
792
+ let suggestion = Disambiguator :: display_for ( kind, path_str) ;
793
+ let help_msg = format ! ( "to link to the {}, use its disambiguator" , kind. descr( id) ) ;
794
+ diag. note ( & note) ;
795
+ if let Some ( sp) = sp {
796
+ diag. span_suggestion ( sp, & help_msg, suggestion, Applicability :: MaybeIncorrect ) ;
797
+ } else {
798
+ diag. help ( & format ! ( "{}: {}" , help_msg, suggestion) ) ;
799
+ }
800
+ } ) ;
801
+ continue ;
802
+ }
803
+ }
804
+ }
805
+
792
806
// item can be non-local e.g. when using #[doc(primitive = "pointer")]
793
807
if let Some ( ( src_id, dst_id) ) = res
794
808
. opt_def_id ( )
@@ -837,6 +851,94 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
837
851
}
838
852
}
839
853
854
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
855
+ enum Disambiguator {
856
+ Kind ( DefKind ) ,
857
+ Namespace ( Namespace ) ,
858
+ }
859
+
860
+ impl Disambiguator {
861
+ /// (disambiguator, path_str)
862
+ fn from_str ( link : & str ) -> Result < ( Self , & str ) , ( ) > {
863
+ use Disambiguator :: { Kind , Namespace as NS } ;
864
+
865
+ let find_suffix = || {
866
+ let suffixes = [
867
+ ( "!()" , DefKind :: Macro ( MacroKind :: Bang ) ) ,
868
+ ( "()" , DefKind :: Fn ) ,
869
+ ( "!" , DefKind :: Macro ( MacroKind :: Bang ) ) ,
870
+ ] ;
871
+ for & ( suffix, kind) in & suffixes {
872
+ if link. ends_with ( suffix) {
873
+ return Ok ( ( Kind ( kind) , link. trim_end_matches ( suffix) ) ) ;
874
+ }
875
+ }
876
+ Err ( ( ) )
877
+ } ;
878
+
879
+ if let Some ( idx) = link. find ( '@' ) {
880
+ let ( prefix, rest) = link. split_at ( idx) ;
881
+ let d = match prefix {
882
+ "struct" => Kind ( DefKind :: Struct ) ,
883
+ "enum" => Kind ( DefKind :: Enum ) ,
884
+ "trait" => Kind ( DefKind :: Trait ) ,
885
+ "union" => Kind ( DefKind :: Union ) ,
886
+ "module" | "mod" => Kind ( DefKind :: Mod ) ,
887
+ "const" | "constant" => Kind ( DefKind :: Const ) ,
888
+ "static" => Kind ( DefKind :: Static ) ,
889
+ "function" | "fn" | "method" => Kind ( DefKind :: Fn ) ,
890
+ "derive" => Kind ( DefKind :: Macro ( MacroKind :: Derive ) ) ,
891
+ "type" => NS ( Namespace :: TypeNS ) ,
892
+ "value" => NS ( Namespace :: ValueNS ) ,
893
+ "macro" => NS ( Namespace :: MacroNS ) ,
894
+ _ => return find_suffix ( ) ,
895
+ } ;
896
+ Ok ( ( d, & rest[ 1 ..] ) )
897
+ } else {
898
+ find_suffix ( )
899
+ }
900
+ }
901
+
902
+ fn display_for ( kind : DefKind , path_str : & str ) -> String {
903
+ if kind == DefKind :: Macro ( MacroKind :: Bang ) {
904
+ return format ! ( "{}!" , path_str) ;
905
+ } else if kind == DefKind :: Fn || kind == DefKind :: AssocFn {
906
+ return format ! ( "{}()" , path_str) ;
907
+ }
908
+ let prefix = match kind {
909
+ DefKind :: Struct => "struct" ,
910
+ DefKind :: Enum => "enum" ,
911
+ DefKind :: Trait => "trait" ,
912
+ DefKind :: Union => "union" ,
913
+ DefKind :: Mod => "mod" ,
914
+ DefKind :: Const | DefKind :: ConstParam | DefKind :: AssocConst | DefKind :: AnonConst => {
915
+ "const"
916
+ }
917
+ DefKind :: Static => "static" ,
918
+ DefKind :: Macro ( MacroKind :: Derive ) => "derive" ,
919
+ // Now handle things that don't have a specific disambiguator
920
+ _ => match kind
921
+ . ns ( )
922
+ . expect ( "tried to calculate a disambiguator for a def without a namespace?" )
923
+ {
924
+ Namespace :: TypeNS => "type" ,
925
+ Namespace :: ValueNS => "value" ,
926
+ Namespace :: MacroNS => "macro" ,
927
+ } ,
928
+ } ;
929
+ format ! ( "{}@{}" , prefix, path_str)
930
+ }
931
+
932
+ fn ns ( self ) -> Namespace {
933
+ match self {
934
+ Self :: Namespace ( n) => n,
935
+ Self :: Kind ( k) => {
936
+ k. ns ( ) . expect ( "only DefKinds with a valid namespace can be disambiguators" )
937
+ }
938
+ }
939
+ }
940
+ }
941
+
840
942
/// Reports a diagnostic for an intra-doc link.
841
943
///
842
944
/// If no link range is provided, or the source span of the link cannot be determined, the span of
0 commit comments