@@ -174,7 +174,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
174174 fn resolve (
175175 & self ,
176176 path_str : & str ,
177- disambiguator : Option < & str > ,
177+ disambiguator : Option < Disambiguator > ,
178178 ns : Namespace ,
179179 current_item : & Option < String > ,
180180 parent_id : Option < DefId > ,
@@ -214,7 +214,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
214214 Res :: Def ( DefKind :: Mod , _) => {
215215 // This resolved to a module, but if we were passed `type@`,
216216 // we want primitive types to take precedence instead.
217- if disambiguator == Some ( "type" ) {
217+ if disambiguator == Some ( Disambiguator :: Namespace ( Namespace :: TypeNS ) ) {
218218 if let Some ( prim) = is_primitive ( path_str, ns) {
219219 if extra_fragment. is_some ( ) {
220220 return Err ( ErrorKind :: AnchorFailure ( AnchorFailure :: Primitive ) ) ;
@@ -575,47 +575,14 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
575575 } ;
576576 let resolved_self;
577577 let mut path_str;
578- let mut disambiguator = None ;
578+ let disambiguator;
579579 let ( res, fragment) = {
580- let mut kind = None ;
581- path_str = if let Some ( prefix) =
582- [ "struct@" , "enum@" , "type@" , "trait@" , "union@" , "module@" , "mod@" ]
583- . iter ( )
584- . find ( |p| link. starts_with ( * * p) )
585- {
586- kind = Some ( TypeNS ) ;
587- disambiguator = Some ( & prefix[ ..prefix. len ( ) - 1 ] ) ;
588- link. trim_start_matches ( prefix)
589- } else if let Some ( prefix) =
590- [ "const@" , "static@" , "value@" , "function@" , "fn@" , "method@" ]
591- . iter ( )
592- . find ( |p| link. starts_with ( * * p) )
593- {
594- kind = Some ( ValueNS ) ;
595- disambiguator = Some ( & prefix[ ..prefix. len ( ) - 1 ] ) ;
596- link. trim_start_matches ( prefix)
597- } else if link. ends_with ( "!()" ) {
598- kind = Some ( MacroNS ) ;
599- disambiguator = Some ( "bang" ) ;
600- link. trim_end_matches ( "!()" )
601- } else if link. ends_with ( "()" ) {
602- kind = Some ( ValueNS ) ;
603- disambiguator = Some ( "fn" ) ;
604- link. trim_end_matches ( "()" )
605- } else if link. starts_with ( "macro@" ) {
606- kind = Some ( MacroNS ) ;
607- disambiguator = Some ( "macro" ) ;
608- link. trim_start_matches ( "macro@" )
609- } else if link. starts_with ( "derive@" ) {
610- kind = Some ( MacroNS ) ;
611- disambiguator = Some ( "derive" ) ;
612- link. trim_start_matches ( "derive@" )
613- } else if link. ends_with ( '!' ) {
614- kind = Some ( MacroNS ) ;
615- disambiguator = Some ( "bang" ) ;
616- link. trim_end_matches ( '!' )
580+ path_str = if let Ok ( ( d, path) ) = Disambiguator :: from_str ( & link) {
581+ disambiguator = Some ( d) ;
582+ path
617583 } else {
618- & link[ ..]
584+ disambiguator = None ;
585+ & link
619586 }
620587 . trim ( ) ;
621588
@@ -648,7 +615,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
648615 }
649616 }
650617
651- match kind {
618+ match disambiguator . map ( Disambiguator :: ns ) {
652619 Some ( ns @ ValueNS ) => {
653620 match self . resolve (
654621 path_str,
@@ -796,34 +763,31 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
796763 debug ! ( "saw kind {:?} with disambiguator {:?}" , kind, disambiguator) ;
797764 // NOTE: this relies on the fact that `''` is never parsed as a disambiguator
798765 // NOTE: this needs to be kept in sync with the disambiguator parsing
799- match ( kind, disambiguator. unwrap_or_default ( ) . trim_end_matches ( "@" ) ) {
800- | ( DefKind :: Struct , "struct" )
801- | ( DefKind :: Enum , "enum" )
802- | ( DefKind :: Trait , "trait" )
803- | ( DefKind :: Union , "union" )
804- | ( DefKind :: Mod , "mod" | "module" )
805- | ( DefKind :: Const | DefKind :: ConstParam | DefKind :: AssocConst | DefKind :: AnonConst , "const" )
806- | ( DefKind :: Static , "static" )
766+ match ( kind, disambiguator) {
767+ | ( DefKind :: Const | DefKind :: ConstParam | DefKind :: AssocConst | DefKind :: AnonConst , Some ( Disambiguator :: Kind ( DefKind :: Const ) ) )
807768 // NOTE: this allows 'method' to mean both normal functions and associated functions
808769 // This can't cause ambiguity because both are in the same namespace.
809- | ( DefKind :: Fn | DefKind :: AssocFn , "fn" | "function" | "method" )
810- | ( DefKind :: Macro ( MacroKind :: Bang ) , "bang" )
811- | ( DefKind :: Macro ( MacroKind :: Derive ) , "derive" )
770+ | ( DefKind :: Fn | DefKind :: AssocFn , Some ( Disambiguator :: Kind ( DefKind :: Fn ) ) )
812771 // These are namespaces; allow anything in the namespace to match
813- | ( _, "type" | "macro" | "value" )
772+ | ( _, Some ( Disambiguator :: Namespace ( _ ) ) )
814773 // If no disambiguator given, allow anything
815- | ( _, "" )
774+ | ( _, None )
816775 // All of these are valid, so do nothing
817776 => { }
818- ( _, disambiguator) => {
777+ ( _, Some ( Disambiguator :: Kind ( expected) ) ) if kind == expected => { }
778+ ( _, Some ( expected) ) => {
819779 // The resolved item did not match the disambiguator; give a better error than 'not found'
820780 let msg = format ! ( "unresolved link to `{}`" , path_str) ;
821781 report_diagnostic ( cx, & msg, & item, & dox, link_range, |diag, sp| {
822- let msg = format ! ( "this link resolved to {} {}, which did not match the disambiguator '{}'" , kind. article( ) , kind. descr( id) , disambiguator) ;
782+ // HACK(jynelson): by looking at the source I saw the DefId we pass
783+ // for `expected.descr()` doesn't matter, since it's not a crate
784+ let note = format ! ( "this link resolved to {} {}, which is not {} {}" , kind. article( ) , kind. descr( id) , expected. article( ) , expected. descr( id) ) ;
785+ let suggestion = Disambiguator :: display_for ( kind, path_str) ;
786+ let help_msg = format ! ( "to link to the {}, use its disambiguator" , kind. descr( id) ) ;
787+ diag. note ( & note) ;
823788 if let Some ( sp) = sp {
824- diag. span_note ( sp, & msg) ;
825- } else {
826- diag. note ( & msg) ;
789+ diag. span_suggestion ( sp, & help_msg, suggestion, Applicability :: MaybeIncorrect ) ;
790+ diag. set_sort_span ( sp) ;
827791 }
828792 } ) ;
829793 continue ;
@@ -879,6 +843,109 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
879843 }
880844}
881845
846+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
847+ enum Disambiguator {
848+ Kind ( DefKind ) ,
849+ Namespace ( Namespace ) ,
850+ }
851+
852+ impl Disambiguator {
853+ /// (disambiguator, path_str)
854+ fn from_str ( link : & str ) -> Result < ( Self , & str ) , ( ) > {
855+ use Disambiguator :: { Kind , Namespace as NS } ;
856+
857+ let find_suffix = || {
858+ let suffixes = [
859+ ( "!()" , DefKind :: Macro ( MacroKind :: Bang ) ) ,
860+ ( "()" , DefKind :: Fn ) ,
861+ ( "!" , DefKind :: Macro ( MacroKind :: Bang ) ) ,
862+ ] ;
863+ for & ( suffix, kind) in & suffixes {
864+ if link. ends_with ( suffix) {
865+ return Ok ( ( Kind ( kind) , link. trim_end_matches ( suffix) ) ) ;
866+ }
867+ }
868+ Err ( ( ) )
869+ } ;
870+
871+ if let Some ( idx) = link. find ( '@' ) {
872+ let ( prefix, rest) = link. split_at ( idx) ;
873+ let d = match prefix {
874+ "struct" => Kind ( DefKind :: Struct ) ,
875+ "enum" => Kind ( DefKind :: Enum ) ,
876+ "trait" => Kind ( DefKind :: Trait ) ,
877+ "union" => Kind ( DefKind :: Union ) ,
878+ "module" | "mod" => Kind ( DefKind :: Mod ) ,
879+ "const" | "constant" => Kind ( DefKind :: Const ) ,
880+ "static" => Kind ( DefKind :: Static ) ,
881+ "function" | "fn" | "method" => Kind ( DefKind :: Fn ) ,
882+ "derive" => Kind ( DefKind :: Macro ( MacroKind :: Derive ) ) ,
883+ "type" => NS ( Namespace :: TypeNS ) ,
884+ "value" => NS ( Namespace :: ValueNS ) ,
885+ "macro" => NS ( Namespace :: MacroNS ) ,
886+ _ => return find_suffix ( ) ,
887+ } ;
888+ Ok ( ( d, & rest[ 1 ..] ) )
889+ } else {
890+ find_suffix ( )
891+ }
892+ }
893+
894+ fn display_for ( kind : DefKind , path_str : & str ) -> String {
895+ if kind == DefKind :: Macro ( MacroKind :: Bang ) {
896+ return format ! ( "{}!" , path_str) ;
897+ }
898+ let prefix = match kind {
899+ DefKind :: Struct => "struct" ,
900+ DefKind :: Enum => "enum" ,
901+ DefKind :: Trait => "trait" ,
902+ DefKind :: Union => "union" ,
903+ DefKind :: Mod => "mod" ,
904+ DefKind :: Const | DefKind :: ConstParam | DefKind :: AssocConst | DefKind :: AnonConst => {
905+ "const"
906+ }
907+ DefKind :: Static => "static" ,
908+ DefKind :: Fn | DefKind :: AssocFn => "fn" ,
909+ DefKind :: Macro ( MacroKind :: Derive ) => "derive" ,
910+ // Now handle things that don't have a specific disambiguator
911+ _ => match kind
912+ . ns ( )
913+ . expect ( "tried to calculate a disambiguator for a def without a namespace?" )
914+ {
915+ Namespace :: TypeNS => "type" ,
916+ Namespace :: ValueNS => "value" ,
917+ Namespace :: MacroNS => "macro" ,
918+ } ,
919+ } ;
920+ format ! ( "{}@{}" , prefix, path_str)
921+ }
922+
923+ fn ns ( self ) -> Namespace {
924+ match self {
925+ Self :: Namespace ( n) => n,
926+ Self :: Kind ( k) => {
927+ k. ns ( ) . expect ( "only DefKinds with a valid namespace can be disambiguators" )
928+ }
929+ }
930+ }
931+
932+ fn article ( & self ) -> & ' static str {
933+ match self {
934+ Self :: Namespace ( _) => "a" ,
935+ Self :: Kind ( kind) => kind. article ( ) ,
936+ }
937+ }
938+
939+ fn descr ( & self , def_id : DefId ) -> & ' static str {
940+ match self {
941+ Self :: Namespace ( Namespace :: TypeNS ) => "type" ,
942+ Self :: Namespace ( Namespace :: ValueNS ) => "value" ,
943+ Self :: Namespace ( Namespace :: MacroNS ) => "macro" ,
944+ Self :: Kind ( kind) => kind. descr ( def_id) ,
945+ }
946+ }
947+ }
948+
882949/// Reports a diagnostic for an intra-doc link.
883950///
884951/// If no link range is provided, or the source span of the link cannot be determined, the span of
0 commit comments