@@ -8,7 +8,7 @@ use rustc_hir::def::{
88 Namespace :: { self , * } ,
99 PerNS , Res ,
1010} ;
11- use rustc_hir:: def_id:: DefId ;
11+ use rustc_hir:: def_id:: { CrateNum , DefId } ;
1212use rustc_middle:: ty;
1313use rustc_resolve:: ParentScope ;
1414use rustc_session:: lint:: {
@@ -767,17 +767,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
767767 self . mod_ids . push ( item. def_id ) ;
768768 }
769769
770- #[ cfg( debug_assertions) ]
771- for attr in & item. attrs . doc_strings {
772- if let Some ( id) = attr. parent_module {
773- trace ! ( "docs {:?} came from {:?}" , attr. doc, id) ;
774- } else {
775- debug ! ( "no parent found for {:?}" , attr. doc) ;
776- }
777- }
778- let dox = item. attrs . collapsed_doc_value ( ) . unwrap_or_else ( String :: new) ;
779- //trace!("got documentation '{}'", dox);
780-
781770 // find item's parent to resolve `Self` in item's docs below
782771 let parent_name = self . cx . as_local_hir_id ( item. def_id ) . and_then ( |item_hir| {
783772 let parent_hir = self . cx . tcx . hir ( ) . get_parent_item ( item_hir) ;
@@ -815,16 +804,53 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
815804 }
816805 } ) ;
817806
818- for ( ori_link, link_range) in markdown_links ( & dox) {
819- self . resolve_link (
820- & mut item,
821- & dox,
822- & current_item,
823- parent_node,
824- & parent_name,
825- ori_link,
826- link_range,
827- ) ;
807+ // We want to resolve in the lexical scope of the documentation.
808+ // In the presence of re-exports, this is not the same as the module of the item.
809+ // Rather than merging all documentation into one, resolve it one attribute at a time
810+ // so we know which module it came from.
811+ let mut attrs = item. attrs . doc_strings . iter ( ) . peekable ( ) ;
812+ while let Some ( attr) = attrs. next ( ) {
813+ // `collapse_docs` does not have the behavior we want:
814+ // we want `///` and `#[doc]` to count as the same attribute,
815+ // but currently it will treat them as separate.
816+ // As a workaround, combine all attributes with the same parent module into the same attribute.
817+ let mut combined_docs = attr. doc . clone ( ) ;
818+ loop {
819+ match attrs. peek ( ) {
820+ Some ( next) if next. parent_module == attr. parent_module => {
821+ combined_docs. push ( '\n' ) ;
822+ combined_docs. push_str ( & attrs. next ( ) . unwrap ( ) . doc ) ;
823+ }
824+ _ => break ,
825+ }
826+ }
827+ debug ! ( "combined_docs={}" , combined_docs) ;
828+
829+ let ( krate, parent_node) = if let Some ( id) = attr. parent_module {
830+ trace ! ( "docs {:?} came from {:?}" , attr. doc, id) ;
831+ ( id. krate , Some ( id) )
832+ } else {
833+ trace ! ( "no parent found for {:?}" , attr. doc) ;
834+ ( item. def_id . krate , parent_node)
835+ } ;
836+ // NOTE: if there are links that start in one crate and end in another, this will not resolve them.
837+ // This is a degenerate case and it's not supported by rustdoc.
838+ // FIXME: this will break links that start in `#[doc = ...]` and end as a sugared doc. Should this be supported?
839+ for ( ori_link, link_range) in markdown_links ( & combined_docs) {
840+ let link = self . resolve_link (
841+ & item,
842+ & combined_docs,
843+ & current_item,
844+ parent_node,
845+ & parent_name,
846+ krate,
847+ ori_link,
848+ link_range,
849+ ) ;
850+ if let Some ( link) = link {
851+ item. attrs . links . push ( link) ;
852+ }
853+ }
828854 }
829855
830856 if item. is_mod ( ) && !item. attrs . inner_docs {
@@ -846,36 +872,37 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
846872impl LinkCollector < ' _ , ' _ > {
847873 fn resolve_link (
848874 & self ,
849- item : & mut Item ,
875+ item : & Item ,
850876 dox : & str ,
851877 current_item : & Option < String > ,
852878 parent_node : Option < DefId > ,
853879 parent_name : & Option < String > ,
880+ krate : CrateNum ,
854881 ori_link : String ,
855882 link_range : Option < Range < usize > > ,
856- ) {
883+ ) -> Option < ItemLink > {
857884 trace ! ( "considering link '{}'" , ori_link) ;
858885
859886 // Bail early for real links.
860887 if ori_link. contains ( '/' ) {
861- return ;
888+ return None ;
862889 }
863890
864891 // [] is mostly likely not supposed to be a link
865892 if ori_link. is_empty ( ) {
866- return ;
893+ return None ;
867894 }
868895
869896 let cx = self . cx ;
870897 let link = ori_link. replace ( "`" , "" ) ;
871898 let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
872899 let ( link, extra_fragment) = if parts. len ( ) > 2 {
873900 anchor_failure ( cx, & item, & link, dox, link_range, AnchorFailure :: MultipleAnchors ) ;
874- return ;
901+ return None ;
875902 } else if parts. len ( ) == 2 {
876903 if parts[ 0 ] . trim ( ) . is_empty ( ) {
877904 // This is an anchor to an element of the current page, nothing to do in here!
878- return ;
905+ return None ;
879906 }
880907 ( parts[ 0 ] , Some ( parts[ 1 ] . to_owned ( ) ) )
881908 } else {
@@ -896,7 +923,7 @@ impl LinkCollector<'_, '_> {
896923 . trim ( ) ;
897924
898925 if path_str. contains ( |ch : char | !( ch. is_alphanumeric ( ) || ch == ':' || ch == '_' ) ) {
899- return ;
926+ return None ;
900927 }
901928
902929 // We stripped `()` and `!` when parsing the disambiguator.
@@ -936,7 +963,7 @@ impl LinkCollector<'_, '_> {
936963 link_range,
937964 smallvec ! [ err_kind] ,
938965 ) ;
939- return ;
966+ return None ;
940967 } ;
941968
942969 // replace `Self` with suitable item's parent name
@@ -955,7 +982,7 @@ impl LinkCollector<'_, '_> {
955982 // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root.
956983 resolved_self = format ! ( "self::{}" , & path_str[ "crate::" . len( ) ..] ) ;
957984 path_str = & resolved_self;
958- module_id = DefId { krate : item . def_id . krate , index : CRATE_DEF_INDEX } ;
985+ module_id = DefId { krate, index : CRATE_DEF_INDEX } ;
959986 }
960987
961988 match self . resolve_with_disambiguator (
@@ -970,7 +997,7 @@ impl LinkCollector<'_, '_> {
970997 link_range. clone ( ) ,
971998 ) {
972999 Some ( x) => x,
973- None => return ,
1000+ None => return None ,
9741001 }
9751002 } ;
9761003
@@ -994,15 +1021,15 @@ impl LinkCollector<'_, '_> {
9941021 link_range,
9951022 AnchorFailure :: RustdocAnchorConflict ( prim) ,
9961023 ) ;
997- return ;
1024+ return None ;
9981025 }
9991026 res = prim;
10001027 fragment = Some ( path. to_owned ( ) ) ;
10011028 } else {
10021029 // `[char]` when a `char` module is in scope
10031030 let candidates = vec ! [ res, prim] ;
10041031 ambiguity_error ( cx, & item, path_str, dox, link_range, candidates) ;
1005- return ;
1032+ return None ;
10061033 }
10071034 }
10081035 }
@@ -1026,16 +1053,11 @@ impl LinkCollector<'_, '_> {
10261053 if let Res :: PrimTy ( ..) = res {
10271054 match disambiguator {
10281055 Some ( Disambiguator :: Primitive | Disambiguator :: Namespace ( _) ) | None => {
1029- item. attrs . links . push ( ItemLink {
1030- link : ori_link,
1031- link_text,
1032- did : None ,
1033- fragment,
1034- } ) ;
1056+ Some ( ItemLink { link : ori_link, link_text, did : None , fragment } )
10351057 }
10361058 Some ( other) => {
10371059 report_mismatch ( other, Disambiguator :: Primitive ) ;
1038- return ;
1060+ None
10391061 }
10401062 }
10411063 } else {
@@ -1058,7 +1080,7 @@ impl LinkCollector<'_, '_> {
10581080 ( actual, Some ( Disambiguator :: Kind ( expected) ) ) if actual == expected => { }
10591081 ( _, Some ( specified @ Disambiguator :: Kind ( _) | specified @ Disambiguator :: Primitive ) ) => {
10601082 report_mismatch ( specified, Disambiguator :: Kind ( kind) ) ;
1061- return ;
1083+ return None ;
10621084 }
10631085 }
10641086 }
@@ -1081,14 +1103,14 @@ impl LinkCollector<'_, '_> {
10811103 }
10821104 }
10831105 let id = register_res ( cx, res) ;
1084- item . attrs . links . push ( ItemLink { link : ori_link, link_text, did : Some ( id) , fragment } ) ;
1106+ Some ( ItemLink { link : ori_link, link_text, did : Some ( id) , fragment } )
10851107 }
10861108 }
10871109
10881110 fn resolve_with_disambiguator (
10891111 & self ,
10901112 disambiguator : Option < Disambiguator > ,
1091- item : & mut Item ,
1113+ item : & Item ,
10921114 dox : & str ,
10931115 path_str : & str ,
10941116 current_item : & Option < String > ,
0 commit comments