@@ -574,9 +574,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
574574 } ;
575575 let resolved_self;
576576 let mut path_str;
577+ let mut disambiguator = None ;
577578 let ( res, fragment) = {
578579 let mut kind = None ;
579- let mut disambiguator = None ;
580580 path_str = if let Some ( prefix) =
581581 [ "struct@" , "enum@" , "type@" , "trait@" , "union@" , "module@" , "mod@" ]
582582 . iter ( )
@@ -595,6 +595,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
595595 link. trim_start_matches ( prefix)
596596 } else if link. ends_with ( "!()" ) {
597597 kind = Some ( MacroNS ) ;
598+ disambiguator = Some ( "bang" ) ;
598599 link. trim_end_matches ( "!()" )
599600 } else if link. ends_with ( "()" ) {
600601 kind = Some ( ValueNS ) ;
@@ -610,7 +611,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
610611 link. trim_start_matches ( "derive@" )
611612 } else if link. ends_with ( '!' ) {
612613 kind = Some ( MacroNS ) ;
613- disambiguator = Some ( "macro " ) ;
614+ disambiguator = Some ( "bang " ) ;
614615 link. trim_end_matches ( '!' )
615616 } else {
616617 & link[ ..]
@@ -789,6 +790,46 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
789790 } else {
790791 debug ! ( "intra-doc link to {} resolved to {:?}" , path_str, res) ;
791792
793+ // Disallow e.g. linking to enums with `struct@`
794+ if let Res :: Def ( kind, id) = res {
795+ debug ! ( "saw kind {:?} with disambiguator {:?}" , kind, disambiguator) ;
796+ // NOTE: this relies on the fact that `''` is never parsed as a disambiguator
797+ // NOTE: this needs to be kept in sync with the disambiguator parsing
798+ match ( kind, disambiguator. unwrap_or_default ( ) . trim_end_matches ( "@" ) ) {
799+ | ( DefKind :: Struct , "struct" )
800+ | ( DefKind :: Enum , "enum" )
801+ | ( DefKind :: Trait , "trait" )
802+ | ( DefKind :: Union , "union" )
803+ | ( DefKind :: Mod , "mod" | "module" )
804+ | ( DefKind :: Const | DefKind :: ConstParam | DefKind :: AssocConst | DefKind :: AnonConst , "const" )
805+ | ( DefKind :: Static , "static" )
806+ // NOTE: this allows 'method' to mean both normal functions and associated functions
807+ // This can't cause ambiguity because both are in the same namespace.
808+ | ( DefKind :: Fn | DefKind :: AssocFn , "fn" | "function" | "method" )
809+ | ( DefKind :: Macro ( MacroKind :: Bang ) , "bang" )
810+ | ( DefKind :: Macro ( MacroKind :: Derive ) , "derive" )
811+ // These are namespaces; allow anything in the namespace to match
812+ | ( _, "type" | "macro" | "value" )
813+ // If no disambiguator given, allow anything
814+ | ( _, "" )
815+ // All of these are valid, so do nothing
816+ => { }
817+ ( _, disambiguator) => {
818+ // The resolved item did not match the disambiguator; give a better error than 'not found'
819+ let msg = format ! ( "unresolved link to `{}`" , path_str) ;
820+ report_diagnostic ( cx, & msg, & item, & dox, link_range, |diag, sp| {
821+ let msg = format ! ( "this item resolved to {} {}, which did not match the disambiguator '{}'" , kind. article( ) , kind. descr( id) , disambiguator) ;
822+ if let Some ( sp) = sp {
823+ diag. span_note ( sp, & msg) ;
824+ } else {
825+ diag. note ( & msg) ;
826+ }
827+ } ) ;
828+ continue ;
829+ }
830+ }
831+ }
832+
792833 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
793834 if let Some ( ( src_id, dst_id) ) = res
794835 . opt_def_id ( )
0 commit comments