@@ -368,55 +368,28 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
368368 }
369369
370370 /// Given a primitive type, try to resolve an associated item.
371- ///
372- /// HACK(jynelson): `item_str` is passed in instead of derived from `item_name` so the
373- /// lifetimes on `&'path` will work.
374371 fn resolve_primitive_associated_item (
375372 & self ,
376373 prim_ty : PrimitiveType ,
377374 ns : Namespace ,
378- module_id : DefId ,
379375 item_name : Symbol ,
380- item_str : & ' path str ,
381- ) -> Result < ( Res , Option < String > ) , ErrorKind < ' path > > {
376+ ) -> Option < ( Res , String , Option < ( DefKind , DefId ) > ) > {
382377 let tcx = self . cx . tcx ;
383378
384- prim_ty
385- . impls ( tcx)
386- . into_iter ( )
387- . find_map ( |& impl_| {
388- tcx. associated_items ( impl_)
389- . find_by_name_and_namespace ( tcx, Ident :: with_dummy_span ( item_name) , ns, impl_)
390- . map ( |item| {
391- let kind = item. kind ;
392- self . kind_side_channel . set ( Some ( ( kind. as_def_kind ( ) , item. def_id ) ) ) ;
393- match kind {
394- ty:: AssocKind :: Fn => "method" ,
395- ty:: AssocKind :: Const => "associatedconstant" ,
396- ty:: AssocKind :: Type => "associatedtype" ,
397- }
398- } )
399- . map ( |out| {
400- (
401- Res :: Primitive ( prim_ty) ,
402- Some ( format ! ( "{}#{}.{}" , prim_ty. as_str( ) , out, item_str) ) ,
403- )
404- } )
405- } )
406- . ok_or_else ( || {
407- debug ! (
408- "returning primitive error for {}::{} in {} namespace" ,
409- prim_ty. as_str( ) ,
410- item_name,
411- ns. descr( )
412- ) ;
413- ResolutionFailure :: NotResolved {
414- module_id,
415- partial_res : Some ( Res :: Primitive ( prim_ty) ) ,
416- unresolved : item_str. into ( ) ,
417- }
418- . into ( )
419- } )
379+ prim_ty. impls ( tcx) . into_iter ( ) . find_map ( |& impl_| {
380+ tcx. associated_items ( impl_)
381+ . find_by_name_and_namespace ( tcx, Ident :: with_dummy_span ( item_name) , ns, impl_)
382+ . map ( |item| {
383+ let kind = item. kind ;
384+ let out = match kind {
385+ ty:: AssocKind :: Fn => "method" ,
386+ ty:: AssocKind :: Const => "associatedconstant" ,
387+ ty:: AssocKind :: Type => "associatedtype" ,
388+ } ;
389+ let fragment = format ! ( "{}#{}.{}" , prim_ty. as_str( ) , out, item_name) ;
390+ ( Res :: Primitive ( prim_ty) , fragment, Some ( ( kind. as_def_kind ( ) , item. def_id ) ) )
391+ } )
392+ } )
420393 }
421394
422395 /// Resolves a string as a macro.
@@ -490,8 +463,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
490463 module_id : DefId ,
491464 extra_fragment : & Option < String > ,
492465 ) -> Result < ( Res , Option < String > ) , ErrorKind < ' path > > {
493- let tcx = self . cx . tcx ;
494-
495466 if let Some ( res) = self . resolve_path ( path_str, ns, module_id) {
496467 match res {
497468 // FIXME(#76467): make this fallthrough to lookup the associated
@@ -534,29 +505,58 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
534505 }
535506 } ) ?;
536507
537- // FIXME: are these both necessary?
538- let ty_res = if let Some ( ty_res) = resolve_primitive ( & path_root, TypeNS )
508+ // FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support
509+ // links to primitives when `#[doc(primitive)]` is present. It should give an ambiguity
510+ // error instead and special case *only* modules with `#[doc(primitive)]`, not all
511+ // primitives.
512+ resolve_primitive ( & path_root, TypeNS )
539513 . or_else ( || self . resolve_path ( & path_root, TypeNS , module_id) )
540- {
541- ty_res
542- } else {
543- // FIXME: this is duplicated on the end of this function.
544- return if ns == Namespace :: ValueNS {
545- self . variant_field ( path_str, module_id)
546- } else {
547- Err ( ResolutionFailure :: NotResolved {
548- module_id,
549- partial_res : None ,
550- unresolved : path_root. into ( ) ,
514+ . and_then ( |ty_res| {
515+ let ( res, fragment, side_channel) =
516+ self . resolve_associated_item ( ty_res, item_name, ns, module_id) ?;
517+ let result = if extra_fragment. is_some ( ) {
518+ let diag_res = side_channel. map_or ( res, |( k, r) | Res :: Def ( k, r) ) ;
519+ Err ( ErrorKind :: AnchorFailure ( AnchorFailure :: RustdocAnchorConflict ( diag_res) ) )
520+ } else {
521+ // HACK(jynelson): `clean` expects the type, not the associated item
522+ // but the disambiguator logic expects the associated item.
523+ // Store the kind in a side channel so that only the disambiguator logic looks at it.
524+ if let Some ( ( kind, id) ) = side_channel {
525+ self . kind_side_channel . set ( Some ( ( kind, id) ) ) ;
526+ }
527+ Ok ( ( res, Some ( fragment) ) )
528+ } ;
529+ Some ( result)
530+ } )
531+ . unwrap_or_else ( || {
532+ if ns == Namespace :: ValueNS {
533+ self . variant_field ( path_str, module_id)
534+ } else {
535+ Err ( ResolutionFailure :: NotResolved {
536+ module_id,
537+ partial_res : None ,
538+ unresolved : path_root. into ( ) ,
539+ }
540+ . into ( ) )
551541 }
552- . into ( ) )
553- } ;
554- } ;
542+ } )
543+ }
544+
545+ /// Returns:
546+ /// - None if no associated item was found
547+ /// - Some((_, _, Some(_))) if an item was found and should go through a side channel
548+ /// - Some((_, _, None)) otherwise
549+ fn resolve_associated_item (
550+ & mut self ,
551+ root_res : Res ,
552+ item_name : Symbol ,
553+ ns : Namespace ,
554+ module_id : DefId ,
555+ ) -> Option < ( Res , String , Option < ( DefKind , DefId ) > ) > {
556+ let tcx = self . cx . tcx ;
555557
556- let res = match ty_res {
557- Res :: Primitive ( prim) => Some (
558- self . resolve_primitive_associated_item ( prim, ns, module_id, item_name, item_str) ,
559- ) ,
558+ match root_res {
559+ Res :: Primitive ( prim) => self . resolve_primitive_associated_item ( prim, ns, item_name) ,
560560 Res :: Def (
561561 DefKind :: Struct
562562 | DefKind :: Union
@@ -599,59 +599,42 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
599599 ty:: AssocKind :: Const => "associatedconstant" ,
600600 ty:: AssocKind :: Type => "associatedtype" ,
601601 } ;
602- Some ( if extra_fragment. is_some ( ) {
603- Err ( ErrorKind :: AnchorFailure ( AnchorFailure :: RustdocAnchorConflict ( ty_res) ) )
604- } else {
605- // HACK(jynelson): `clean` expects the type, not the associated item
606- // but the disambiguator logic expects the associated item.
607- // Store the kind in a side channel so that only the disambiguator logic looks at it.
608- self . kind_side_channel . set ( Some ( ( kind. as_def_kind ( ) , id) ) ) ;
609- Ok ( ( ty_res, Some ( format ! ( "{}.{}" , out, item_str) ) ) )
610- } )
611- } else if ns == Namespace :: ValueNS {
612- debug ! ( "looking for variants or fields named {} for {:?}" , item_name, did) ;
613- // FIXME(jynelson): why is this different from
614- // `variant_field`?
615- match tcx. type_of ( did) . kind ( ) {
616- ty:: Adt ( def, _) => {
617- let field = if def. is_enum ( ) {
618- def. all_fields ( ) . find ( |item| item. ident . name == item_name)
619- } else {
620- def. non_enum_variant ( )
621- . fields
622- . iter ( )
623- . find ( |item| item. ident . name == item_name)
624- } ;
625- field. map ( |item| {
626- if extra_fragment. is_some ( ) {
627- let res = Res :: Def (
628- if def. is_enum ( ) {
629- DefKind :: Variant
630- } else {
631- DefKind :: Field
632- } ,
633- item. did ,
634- ) ;
635- Err ( ErrorKind :: AnchorFailure (
636- AnchorFailure :: RustdocAnchorConflict ( res) ,
637- ) )
638- } else {
639- Ok ( (
640- ty_res,
641- Some ( format ! (
642- "{}.{}" ,
643- if def. is_enum( ) { "variant" } else { "structfield" } ,
644- item. ident
645- ) ) ,
646- ) )
647- }
648- } )
649- }
650- _ => None ,
651- }
652- } else {
653- None
602+ // HACK(jynelson): `clean` expects the type, not the associated item
603+ // but the disambiguator logic expects the associated item.
604+ // Store the kind in a side channel so that only the disambiguator logic looks at it.
605+ return Some ( (
606+ root_res,
607+ format ! ( "{}.{}" , out, item_name) ,
608+ Some ( ( kind. as_def_kind ( ) , id) ) ,
609+ ) ) ;
610+ }
611+
612+ if ns != Namespace :: ValueNS {
613+ return None ;
654614 }
615+ debug ! ( "looking for variants or fields named {} for {:?}" , item_name, did) ;
616+ // FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?)
617+ // NOTE: it's different from variant_field because it resolves fields and variants,
618+ // not variant fields (2 path segments, not 3).
619+ let def = match tcx. type_of ( did) . kind ( ) {
620+ ty:: Adt ( def, _) => def,
621+ _ => return None ,
622+ } ;
623+ let field = if def. is_enum ( ) {
624+ def. all_fields ( ) . find ( |item| item. ident . name == item_name)
625+ } else {
626+ def. non_enum_variant ( ) . fields . iter ( ) . find ( |item| item. ident . name == item_name)
627+ } ?;
628+ let kind = if def. is_enum ( ) { DefKind :: Variant } else { DefKind :: Field } ;
629+ Some ( (
630+ root_res,
631+ format ! (
632+ "{}.{}" ,
633+ if def. is_enum( ) { "variant" } else { "structfield" } ,
634+ field. ident
635+ ) ,
636+ Some ( ( kind, field. did ) ) ,
637+ ) )
655638 }
656639 Res :: Def ( DefKind :: Trait , did) => tcx
657640 . associated_items ( did)
@@ -669,27 +652,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
669652 }
670653 } ;
671654
672- if extra_fragment. is_some ( ) {
673- Err ( ErrorKind :: AnchorFailure ( AnchorFailure :: RustdocAnchorConflict ( ty_res) ) )
674- } else {
675- let res = Res :: Def ( item. kind . as_def_kind ( ) , item. def_id ) ;
676- Ok ( ( res, Some ( format ! ( "{}.{}" , kind, item_str) ) ) )
677- }
655+ let res = Res :: Def ( item. kind . as_def_kind ( ) , item. def_id ) ;
656+ ( res, format ! ( "{}.{}" , kind, item_name) , None )
678657 } ) ,
679658 _ => None ,
680- } ;
681- res. unwrap_or_else ( || {
682- if ns == Namespace :: ValueNS {
683- self . variant_field ( path_str, module_id)
684- } else {
685- Err ( ResolutionFailure :: NotResolved {
686- module_id,
687- partial_res : Some ( ty_res) ,
688- unresolved : item_str. into ( ) ,
689- }
690- . into ( ) )
691- }
692- } )
659+ }
693660 }
694661
695662 /// Used for reporting better errors.
0 commit comments