@@ -17,6 +17,7 @@ use rustc_span::symbol::Ident;
1717use rustc_span:: symbol:: Symbol ;
1818use rustc_span:: DUMMY_SP ;
1919
20+ use std:: cell:: Cell ;
2021use std:: ops:: Range ;
2122
2223use crate :: clean:: * ;
@@ -62,11 +63,15 @@ struct LinkCollector<'a, 'tcx> {
6263 cx : & ' a DocContext < ' tcx > ,
6364 // NOTE: this may not necessarily be a module in the current crate
6465 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 > > ,
6570}
6671
6772impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
6873 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 ) }
7075 }
7176
7277 fn variant_field (
@@ -174,7 +179,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
174179 fn resolve (
175180 & self ,
176181 path_str : & str ,
177- disambiguator : Option < & str > ,
182+ disambiguator : Option < Disambiguator > ,
178183 ns : Namespace ,
179184 current_item : & Option < String > ,
180185 parent_id : Option < DefId > ,
@@ -214,7 +219,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
214219 Res :: Def ( DefKind :: Mod , _) => {
215220 // This resolved to a module, but if we were passed `type@`,
216221 // we want primitive types to take precedence instead.
217- if disambiguator == Some ( "type" ) {
222+ if disambiguator == Some ( Disambiguator :: Namespace ( Namespace :: TypeNS ) ) {
218223 if let Some ( prim) = is_primitive ( path_str, ns) {
219224 if extra_fragment. is_some ( ) {
220225 return Err ( ErrorKind :: AnchorFailure ( AnchorFailure :: Primitive ) ) ;
@@ -347,6 +352,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
347352 AnchorFailure :: AssocConstant
348353 } ) )
349354 } 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 ( ) ) ) ;
350359 Ok ( ( ty_res, Some ( format ! ( "{}.{}" , out, item_name) ) ) )
351360 }
352361 } else {
@@ -415,7 +424,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
415424 AnchorFailure :: Method
416425 } ) )
417426 } 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) ) ) )
419429 }
420430 } else {
421431 self . variant_field ( path_str, current_item, module_id)
@@ -574,46 +584,14 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
574584 } ;
575585 let resolved_self;
576586 let mut path_str;
587+ let disambiguator;
577588 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
615592 } else {
616- & link[ ..]
593+ disambiguator = None ;
594+ & link
617595 }
618596 . trim ( ) ;
619597
@@ -646,7 +624,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
646624 }
647625 }
648626
649- match kind {
627+ match disambiguator . map ( Disambiguator :: ns ) {
650628 Some ( ns @ ValueNS ) => {
651629 match self . resolve (
652630 path_str,
@@ -789,6 +767,42 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
789767 } else {
790768 debug ! ( "intra-doc link to {} resolved to {:?}" , path_str, res) ;
791769
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+
792806 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
793807 if let Some ( ( src_id, dst_id) ) = res
794808 . opt_def_id ( )
@@ -837,6 +851,94 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
837851 }
838852}
839853
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+
840942/// Reports a diagnostic for an intra-doc link.
841943///
842944/// If no link range is provided, or the source span of the link cannot be determined, the span of
0 commit comments