@@ -3,7 +3,7 @@ use clean::AttributesExt;
33use  std:: cmp:: Ordering ; 
44use  std:: fmt; 
55
6- use  rustc_data_structures:: fx:: FxHashMap ; 
6+ use  rustc_data_structures:: fx:: { FxHashMap ,   FxHashSet } ; 
77use  rustc_hir as  hir; 
88use  rustc_hir:: def:: CtorKind ; 
99use  rustc_hir:: def_id:: DefId ; 
@@ -795,16 +795,18 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
795795    render_assoc_items ( w,  cx,  it,  it. item_id . expect_def_id ( ) ,  AssocItemRender :: All ) ; 
796796
797797    let  cache = cx. cache ( ) ; 
798+     let  mut  extern_crates = FxHashSet :: default ( ) ; 
798799    if  let  Some ( implementors)  = cache. implementors . get ( & it. item_id . expect_def_id ( ) )  { 
799800        // The DefId is for the first Type found with that name. The bool is 
800801        // if any Types with the same name but different DefId have been found. 
801802        let  mut  implementor_dups:  FxHashMap < Symbol ,  ( DefId ,  bool ) >  = FxHashMap :: default ( ) ; 
802803        for  implementor in  implementors { 
803-             match  implementor. inner_impl ( ) . for_  { 
804-                 clean:: Type :: Path  {  ref  path } 
805-                 | clean:: BorrowedRef  {  type_ :  box clean:: Type :: Path  {  ref  path } ,  .. } 
806-                     if  !path. is_assoc_ty ( )  =>
807-                 { 
804+             if  let  Some ( did)  = implementor. inner_impl ( ) . for_ . without_borrowed_ref ( ) . def_id ( cx. cache ( ) )  &&
805+                 !did. is_local ( )  { 
806+                 extern_crates. insert ( did. krate ) ; 
807+             } 
808+             match  implementor. inner_impl ( ) . for_ . without_borrowed_ref ( )  { 
809+                 clean:: Type :: Path  {  ref  path }  if  !path. is_assoc_ty ( )  => { 
808810                    let  did = path. def_id ( ) ; 
809811                    let  & mut  ( prev_did,  ref  mut  has_duplicates)  =
810812                        implementor_dups. entry ( path. last ( ) ) . or_insert ( ( did,  false ) ) ; 
@@ -903,20 +905,96 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
903905        } 
904906    } 
905907
908+     // Include implementors in crates that depend on the current crate. 
909+     // 
910+     // This is complicated by the way rustdoc is invoked, which is basically 
911+     // the same way rustc is invoked: it gets called, one at a time, for each 
912+     // crate. When building the rustdocs for the current crate, rustdoc can 
913+     // see crate metadata for its dependencies, but cannot see metadata for its 
914+     // dependents. 
915+     // 
916+     // To make this work, we generate a "hook" at this stage, and our 
917+     // dependents can "plug in" to it when they build. For simplicity's sake, 
918+     // it's [JSONP]: a JavaScript file with the data we need (and can parse), 
919+     // surrounded by a tiny wrapper that the Rust side ignores, but allows the 
920+     // JavaScript side to include without having to worry about Same Origin 
921+     // Policy. The code for *that* is in `write_shared.rs`. 
922+     // 
923+     // This is further complicated by `#[doc(inline)]`. We want all copies 
924+     // of an inlined trait to reference the same JS file, to address complex 
925+     // dependency graphs like this one (lower crates depend on higher crates): 
926+     // 
927+     // ```text 
928+     //  -------------------------------------------- 
929+     //  |            crate A: trait Foo            | 
930+     //  -------------------------------------------- 
931+     //      |                               | 
932+     //  --------------------------------    | 
933+     //  | crate B: impl A::Foo for Bar |    | 
934+     //  --------------------------------    | 
935+     //      |                               | 
936+     //  --------------------------------------------- 
937+     //  | crate C: #[doc(inline)] use A::Foo as Baz | 
938+     //  |          impl Baz for Quux                | 
939+     //  --------------------------------------------- 
940+     // ``` 
941+     // 
942+     // Basically, we want `C::Baz` and `A::Foo` to show the same set of 
943+     // impls, which is easier if they both treat `/implementors/A/trait.Foo.js` 
944+     // as the Single Source of Truth. 
945+     // 
946+     // We also want the `impl Baz for Quux` to be written to 
947+     // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`, 
948+     // we're going to want to generate plain HTML for `impl Baz for Quux` too, 
949+     // because that'll load faster, and it's better for SEO. And we don't want 
950+     // the same impl to show up twice on the same page. 
951+     // 
952+     // To make this work, the implementors JS file has a structure kinda 
953+     // like this: 
954+     // 
955+     // ```js 
956+     // JSONP({ 
957+     // "B": {"impl A::Foo for Bar"}, 
958+     // "C": {"impl Baz for Quux"}, 
959+     // }); 
960+     // ``` 
961+     // 
962+     // First of all, this means we can rebuild a crate, and it'll replace its own 
963+     // data if something changes. That is, `rustdoc` is idempotent. The other 
964+     // advantage is that we can list the crates that get included in the HTML, 
965+     // and ignore them when doing the JavaScript-based part of rendering. 
966+     // So C's HTML will have something like this: 
967+     // 
968+     // ```html 
969+     // <script type="text/javascript" src="/implementors/A/trait.Foo.js" 
970+     //     data-ignore-extern-crates="A,B" async></script> 
971+     // ``` 
972+     // 
973+     // And, when the JS runs, anything in data-ignore-extern-crates is known 
974+     // to already be in the HTML, and will be ignored. 
975+     // 
976+     // [JSONP]: https://en.wikipedia.org/wiki/JSONP 
906977    let  mut  js_src_path:  UrlPartsBuilder  = std:: iter:: repeat ( ".." ) 
907978        . take ( cx. current . len ( ) ) 
908979        . chain ( std:: iter:: once ( "implementors" ) ) 
909980        . collect ( ) ; 
910-     if  it. item_id . is_local ( )  { 
911-         js_src_path. extend ( cx. current . iter ( ) . copied ( ) ) ; 
981+     if  let  Some ( did)  = it. item_id . as_def_id ( )  &&
982+         let  get_extern = {  || cache. external_paths . get ( & did) . map ( |s| s. 0 . clone ( ) )  }  &&
983+         let  Some ( fqp)  = cache. exact_paths . get ( & did) . cloned ( ) . or_else ( get_extern)  { 
984+         js_src_path. extend ( fqp[ ..fqp. len ( )  - 1 ] . iter ( ) . copied ( ) ) ; 
985+         js_src_path. push_fmt ( format_args ! ( "{}.{}.js" ,  it. type_( ) ,  fqp. last( ) . unwrap( ) ) ) ; 
912986    }  else  { 
913-         let   ( ref  path ,  _ )  = cache . external_paths [ & it . item_id . expect_def_id ( ) ] ; 
914-         js_src_path. extend ( path [ ..path . len ( )  -  1 ] . iter ( ) . copied ( ) ) ; 
987+         js_src_path . extend ( cx . current . iter ( ) . copied ( ) ) ; 
988+         js_src_path. push_fmt ( format_args ! ( "{}.{}.js" ,  it . type_ ( ) ,  it . name . unwrap ( ) ) ) ; 
915989    } 
916-     js_src_path. push_fmt ( format_args ! ( "{}.{}.js" ,  it. type_( ) ,  it. name. unwrap( ) ) ) ; 
990+     let  extern_crates = extern_crates
991+         . into_iter ( ) 
992+         . map ( |cnum| cx. shared . tcx . crate_name ( cnum) . to_string ( ) ) 
993+         . collect :: < Vec < _ > > ( ) 
994+         . join ( "," ) ; 
917995    write ! ( 
918996        w, 
919-         "<script type=\" text/javascript\"  src=\" {src}\"  async></script>" , 
997+         "<script type=\" text/javascript\"  src=\" {src}\"  data-ignore-extern-crates= \" {extern_crates} \"   async></script>" , 
920998        src = js_src_path. finish( ) , 
921999    ) ; 
9221000} 
0 commit comments