@@ -32,6 +32,7 @@ use crate::clean::{
3232 self , types:: ExternalLocation , utils:: find_nearest_parent_module, ExternalCrate , ItemId ,
3333 PrimitiveType ,
3434} ;
35+ use crate :: formats:: cache:: Cache ;
3536use crate :: formats:: item_type:: ItemType ;
3637use crate :: html:: escape:: Escape ;
3738use crate :: html:: render:: Context ;
@@ -581,7 +582,7 @@ fn generate_macro_def_id_path(
581582 cx : & Context < ' _ > ,
582583 root_path : Option < & str > ,
583584) -> Result < ( String , ItemType , Vec < Symbol > ) , HrefError > {
584- let tcx = cx. shared . tcx ;
585+ let tcx = cx. tcx ( ) ;
585586 let crate_name = tcx. crate_name ( def_id. krate ) ;
586587 let cache = cx. cache ( ) ;
587588
@@ -651,92 +652,179 @@ fn generate_macro_def_id_path(
651652 Ok ( ( url, ItemType :: Macro , fqp) )
652653}
653654
655+ fn generate_item_def_id_path (
656+ mut def_id : DefId ,
657+ original_def_id : DefId ,
658+ cx : & Context < ' _ > ,
659+ root_path : Option < & str > ,
660+ original_def_kind : DefKind ,
661+ ) -> Result < ( String , ItemType , Vec < Symbol > ) , HrefError > {
662+ use crate :: rustc_trait_selection:: infer:: TyCtxtInferExt ;
663+ use crate :: rustc_trait_selection:: traits:: query:: normalize:: QueryNormalizeExt ;
664+ use rustc_middle:: traits:: ObligationCause ;
665+
666+ let tcx = cx. tcx ( ) ;
667+ let crate_name = tcx. crate_name ( def_id. krate ) ;
668+
669+ // No need to try to infer the actual parent item if it's not an associated item from the `impl`
670+ // block.
671+ if def_id != original_def_id && matches ! ( tcx. def_kind( def_id) , DefKind :: Impl { .. } ) {
672+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
673+ def_id = infcx
674+ . at ( & ObligationCause :: dummy ( ) , tcx. param_env ( def_id) )
675+ . query_normalize ( ty:: Binder :: dummy ( tcx. type_of ( def_id) . instantiate_identity ( ) ) )
676+ . map ( |resolved| infcx. resolve_vars_if_possible ( resolved. value ) )
677+ . ok ( )
678+ . and_then ( |normalized| normalized. skip_binder ( ) . ty_adt_def ( ) )
679+ . map ( |adt| adt. did ( ) )
680+ . unwrap_or ( def_id) ;
681+ }
682+
683+ let relative: Vec < Symbol > = tcx
684+ . def_path ( def_id)
685+ . data
686+ . into_iter ( )
687+ . filter_map ( |elem| {
688+ // extern blocks (and a few others things) have an empty name.
689+ match elem. data . get_opt_name ( ) {
690+ Some ( s) if !s. is_empty ( ) => Some ( s) ,
691+ _ => None ,
692+ }
693+ } )
694+ . collect ( ) ;
695+ let fqp: Vec < Symbol > = once ( crate_name) . chain ( relative) . collect ( ) ;
696+
697+ let def_kind = tcx. def_kind ( def_id) ;
698+ let shortty = def_kind. into ( ) ;
699+ let module_fqp = to_module_fqp ( shortty, & fqp) ;
700+ let mut is_remote = false ;
701+
702+ let url_parts = url_parts ( cx. cache ( ) , def_id, & module_fqp, & cx. current , & mut is_remote) ?;
703+ let ( url_parts, shortty, fqp) = make_href ( root_path, shortty, url_parts, & fqp, is_remote) ?;
704+ if def_id == original_def_id {
705+ return Ok ( ( url_parts, shortty, fqp) ) ;
706+ }
707+ let kind = ItemType :: from_def_kind ( original_def_kind, Some ( def_kind) ) ;
708+ Ok ( ( format ! ( "{url_parts}#{kind}.{}" , tcx. item_name( original_def_id) ) , shortty, fqp) )
709+ }
710+
711+ fn to_module_fqp ( shortty : ItemType , fqp : & [ Symbol ] ) -> & [ Symbol ] {
712+ if shortty == ItemType :: Module { fqp } else { & fqp[ ..fqp. len ( ) - 1 ] }
713+ }
714+
715+ fn url_parts (
716+ cache : & Cache ,
717+ def_id : DefId ,
718+ module_fqp : & [ Symbol ] ,
719+ relative_to : & [ Symbol ] ,
720+ is_remote : & mut bool ,
721+ ) -> Result < UrlPartsBuilder , HrefError > {
722+ match cache. extern_locations [ & def_id. krate ] {
723+ ExternalLocation :: Remote ( ref s) => {
724+ * is_remote = true ;
725+ let s = s. trim_end_matches ( '/' ) ;
726+ let mut builder = UrlPartsBuilder :: singleton ( s) ;
727+ builder. extend ( module_fqp. iter ( ) . copied ( ) ) ;
728+ Ok ( builder)
729+ }
730+ ExternalLocation :: Local => Ok ( href_relative_parts ( module_fqp, relative_to) . collect ( ) ) ,
731+ ExternalLocation :: Unknown => Err ( HrefError :: DocumentationNotBuilt ) ,
732+ }
733+ }
734+
735+ fn make_href (
736+ root_path : Option < & str > ,
737+ shortty : ItemType ,
738+ mut url_parts : UrlPartsBuilder ,
739+ fqp : & [ Symbol ] ,
740+ is_remote : bool ,
741+ ) -> Result < ( String , ItemType , Vec < Symbol > ) , HrefError > {
742+ if !is_remote && let Some ( root_path) = root_path {
743+ let root = root_path. trim_end_matches ( '/' ) ;
744+ url_parts. push_front ( root) ;
745+ }
746+ debug ! ( ?url_parts) ;
747+ match shortty {
748+ ItemType :: Module => {
749+ url_parts. push ( "index.html" ) ;
750+ }
751+ _ => {
752+ let prefix = shortty. as_str ( ) ;
753+ let last = fqp. last ( ) . unwrap ( ) ;
754+ url_parts. push_fmt ( format_args ! ( "{prefix}.{last}.html" ) ) ;
755+ }
756+ }
757+ Ok ( ( url_parts. finish ( ) , shortty, fqp. to_vec ( ) ) )
758+ }
759+
654760pub ( crate ) fn href_with_root_path (
655- did : DefId ,
761+ original_did : DefId ,
656762 cx : & Context < ' _ > ,
657763 root_path : Option < & str > ,
658764) -> Result < ( String , ItemType , Vec < Symbol > ) , HrefError > {
659765 let tcx = cx. tcx ( ) ;
660- let def_kind = tcx. def_kind ( did ) ;
766+ let def_kind = tcx. def_kind ( original_did ) ;
661767 let did = match def_kind {
662768 DefKind :: AssocTy | DefKind :: AssocFn | DefKind :: AssocConst | DefKind :: Variant => {
663769 // documented on their parent's page
664- tcx. parent ( did )
770+ tcx. parent ( original_did )
665771 }
772+ // If this a constructor, we get the parent (either a struct or a variant) and then
773+ // generate the link for this item.
774+ DefKind :: Ctor ( ..) => return href_with_root_path ( tcx. parent ( original_did) , cx, root_path) ,
666775 DefKind :: ExternCrate => {
667776 // Link to the crate itself, not the `extern crate` item.
668- if let Some ( local_did) = did . as_local ( ) {
777+ if let Some ( local_did) = original_did . as_local ( ) {
669778 tcx. extern_mod_stmt_cnum ( local_did) . unwrap_or ( LOCAL_CRATE ) . as_def_id ( )
670779 } else {
671- did
780+ original_did
672781 }
673782 }
674- _ => did ,
783+ _ => original_did ,
675784 } ;
676785 let cache = cx. cache ( ) ;
677786 let relative_to = & cx. current ;
678- fn to_module_fqp ( shortty : ItemType , fqp : & [ Symbol ] ) -> & [ Symbol ] {
679- if shortty == ItemType :: Module { fqp } else { & fqp[ ..fqp. len ( ) - 1 ] }
680- }
681787
682- if !did. is_local ( )
683- && !cache. effective_visibilities . is_directly_public ( tcx, did)
684- && !cache. document_private
685- && !cache. primitive_locations . values ( ) . any ( |& id| id == did)
686- {
687- return Err ( HrefError :: Private ) ;
788+ if !original_did. is_local ( ) {
789+ // If we are generating an href for the "jump to def" feature, then the only case we want
790+ // to ignore is if the item is `doc(hidden)` because we can't link to it.
791+ if root_path. is_some ( ) {
792+ if tcx. is_doc_hidden ( original_did) {
793+ return Err ( HrefError :: Private ) ;
794+ }
795+ } else if !cache. effective_visibilities . is_directly_public ( tcx, did)
796+ && !cache. document_private
797+ && !cache. primitive_locations . values ( ) . any ( |& id| id == did)
798+ {
799+ return Err ( HrefError :: Private ) ;
800+ }
688801 }
689802
690803 let mut is_remote = false ;
691- let ( fqp, shortty, mut url_parts) = match cache. paths . get ( & did) {
804+ let ( fqp, shortty, url_parts) = match cache. paths . get ( & did) {
692805 Some ( & ( ref fqp, shortty) ) => ( fqp, shortty, {
693806 let module_fqp = to_module_fqp ( shortty, fqp. as_slice ( ) ) ;
694807 debug ! ( ?fqp, ?shortty, ?module_fqp) ;
695808 href_relative_parts ( module_fqp, relative_to) . collect ( )
696809 } ) ,
697810 None => {
698- if let Some ( & ( ref fqp, shortty) ) = cache. external_paths . get ( & did) {
811+ // Associated items are handled differently with "jump to def". The anchor is generated
812+ // directly here whereas for intra-doc links, we have some extra computation being
813+ // performed there.
814+ let def_id_to_get = if root_path. is_some ( ) { original_did } else { did } ;
815+ if let Some ( & ( ref fqp, shortty) ) = cache. external_paths . get ( & def_id_to_get) {
699816 let module_fqp = to_module_fqp ( shortty, fqp) ;
700- (
701- fqp,
702- shortty,
703- match cache. extern_locations [ & did. krate ] {
704- ExternalLocation :: Remote ( ref s) => {
705- is_remote = true ;
706- let s = s. trim_end_matches ( '/' ) ;
707- let mut builder = UrlPartsBuilder :: singleton ( s) ;
708- builder. extend ( module_fqp. iter ( ) . copied ( ) ) ;
709- builder
710- }
711- ExternalLocation :: Local => {
712- href_relative_parts ( module_fqp, relative_to) . collect ( )
713- }
714- ExternalLocation :: Unknown => return Err ( HrefError :: DocumentationNotBuilt ) ,
715- } ,
716- )
817+ ( fqp, shortty, url_parts ( cache, did, module_fqp, relative_to, & mut is_remote) ?)
717818 } else if matches ! ( def_kind, DefKind :: Macro ( _) ) {
718819 return generate_macro_def_id_path ( did, cx, root_path) ;
820+ } else if did. is_local ( ) {
821+ return Err ( HrefError :: Private ) ;
719822 } else {
720- return Err ( HrefError :: NotInExternalCache ) ;
823+ return generate_item_def_id_path ( did , original_did , cx , root_path , def_kind ) ;
721824 }
722825 }
723826 } ;
724- if !is_remote && let Some ( root_path) = root_path {
725- let root = root_path. trim_end_matches ( '/' ) ;
726- url_parts. push_front ( root) ;
727- }
728- debug ! ( ?url_parts) ;
729- match shortty {
730- ItemType :: Module => {
731- url_parts. push ( "index.html" ) ;
732- }
733- _ => {
734- let prefix = shortty. as_str ( ) ;
735- let last = fqp. last ( ) . unwrap ( ) ;
736- url_parts. push_fmt ( format_args ! ( "{prefix}.{last}.html" ) ) ;
737- }
738- }
739- Ok ( ( url_parts. finish ( ) , shortty, fqp. to_vec ( ) ) )
827+ make_href ( root_path, shortty, url_parts, fqp, is_remote)
740828}
741829
742830pub ( crate ) fn href (
0 commit comments