@@ -7,11 +7,15 @@ use rustc_ast::visit::{self, AssocCtxt, Visitor};
77use rustc_ast:: { self as ast, ItemKind } ;
88use rustc_ast_lowering:: ResolverAstLowering ;
99use rustc_hir:: def:: Namespace :: TypeNS ;
10- use rustc_hir:: def_id:: { DefId , LocalDefId , CRATE_DEF_ID } ;
11- use rustc_resolve:: Resolver ;
10+ use rustc_hir:: def:: { DefKind , Res } ;
11+ use rustc_hir:: def_id:: { DefId , DefIdMap , DefIdSet , LocalDefId , CRATE_DEF_ID } ;
12+ use rustc_hir:: TraitCandidate ;
13+ use rustc_middle:: ty:: { DefIdTree , Visibility } ;
14+ use rustc_resolve:: { ParentScope , Resolver } ;
1215use rustc_session:: config:: Externs ;
13- use rustc_span:: { Span , DUMMY_SP } ;
16+ use rustc_span:: { Span , SyntaxContext , DUMMY_SP } ;
1417
18+ use std:: collections:: hash_map:: Entry ;
1519use std:: mem;
1620
1721crate fn early_resolve_intra_doc_links (
@@ -22,15 +26,18 @@ crate fn early_resolve_intra_doc_links(
2226 let mut loader = IntraLinkCrateLoader {
2327 resolver,
2428 current_mod : CRATE_DEF_ID ,
29+ visited_mods : Default :: default ( ) ,
30+ traits_in_scope : Default :: default ( ) ,
2531 all_traits : Default :: default ( ) ,
2632 all_trait_impls : Default :: default ( ) ,
2733 } ;
2834
2935 // Overridden `visit_item` below doesn't apply to the crate root,
30- // so we have to visit its attributes and exports separately.
36+ // so we have to visit its attributes and reexports separately.
3137 loader. load_links_in_attrs ( & krate. attrs , krate. span ) ;
38+ loader. process_module_children_or_reexports ( CRATE_DEF_ID . to_def_id ( ) ) ;
3239 visit:: walk_crate ( & mut loader, krate) ;
33- loader. fill_resolver_caches ( ) ;
40+ loader. add_foreign_traits_in_scope ( ) ;
3441
3542 // FIXME: somehow rustdoc is still missing crates even though we loaded all
3643 // the known necessary crates. Load them all unconditionally until we find a way to fix this.
@@ -46,6 +53,7 @@ crate fn early_resolve_intra_doc_links(
4653 }
4754
4855 ResolverCaches {
56+ traits_in_scope : loader. traits_in_scope ,
4957 all_traits : Some ( loader. all_traits ) ,
5058 all_trait_impls : Some ( loader. all_trait_impls ) ,
5159 }
@@ -54,27 +62,87 @@ crate fn early_resolve_intra_doc_links(
5462struct IntraLinkCrateLoader < ' r , ' ra > {
5563 resolver : & ' r mut Resolver < ' ra > ,
5664 current_mod : LocalDefId ,
65+ visited_mods : DefIdSet ,
66+ traits_in_scope : DefIdMap < Vec < TraitCandidate > > ,
5767 all_traits : Vec < DefId > ,
5868 all_trait_impls : Vec < DefId > ,
5969}
6070
6171impl IntraLinkCrateLoader < ' _ , ' _ > {
62- fn fill_resolver_caches ( & mut self ) {
63- for cnum in self . resolver . cstore ( ) . crates_untracked ( ) {
64- let all_traits = self . resolver . cstore ( ) . traits_in_crate_untracked ( cnum) ;
65- let all_trait_impls = self . resolver . cstore ( ) . trait_impls_in_crate_untracked ( cnum) ;
72+ fn add_traits_in_scope ( & mut self , def_id : DefId ) {
73+ // Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
74+ // Keys in the `traits_in_scope` cache are always module IDs.
75+ if let Entry :: Vacant ( entry) = self . traits_in_scope . entry ( def_id) {
76+ let module = self . resolver . get_nearest_non_block_module ( def_id) ;
77+ let module_id = module. def_id ( ) ;
78+ let entry = if module_id == def_id {
79+ Some ( entry)
80+ } else if let Entry :: Vacant ( entry) = self . traits_in_scope . entry ( module_id) {
81+ Some ( entry)
82+ } else {
83+ None
84+ } ;
85+ if let Some ( entry) = entry {
86+ entry. insert ( self . resolver . traits_in_scope (
87+ None ,
88+ & ParentScope :: module ( module, self . resolver ) ,
89+ SyntaxContext :: root ( ) ,
90+ None ,
91+ ) ) ;
92+ }
93+ }
94+ }
95+
96+ fn add_traits_in_parent_scope ( & mut self , def_id : DefId ) {
97+ if let Some ( module_id) = self . resolver . parent ( def_id) {
98+ self . add_traits_in_scope ( module_id) ;
99+ }
100+ }
101+
102+ /// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
103+ /// That pass filters impls using type-based information, but we don't yet have such
104+ /// information here, so we just conservatively calculate traits in scope for *all* modules
105+ /// having impls in them.
106+ fn add_foreign_traits_in_scope ( & mut self ) {
107+ for cnum in Vec :: from_iter ( self . resolver . cstore ( ) . crates_untracked ( ) ) {
108+ // FIXME: Due to #78696 rustdoc can query traits in scope for any crate root.
109+ self . add_traits_in_scope ( cnum. as_def_id ( ) ) ;
110+
111+ let all_traits = Vec :: from_iter ( self . resolver . cstore ( ) . traits_in_crate_untracked ( cnum) ) ;
112+ let all_trait_impls =
113+ Vec :: from_iter ( self . resolver . cstore ( ) . trait_impls_in_crate_untracked ( cnum) ) ;
114+
115+ // Querying traits in scope is expensive so we try to prune the impl and traits lists
116+ // using privacy, private traits and impls from other crates are never documented in
117+ // the current crate, and links in their doc comments are not resolved.
118+ for & def_id in & all_traits {
119+ if self . resolver . cstore ( ) . visibility_untracked ( def_id) == Visibility :: Public {
120+ self . add_traits_in_parent_scope ( def_id) ;
121+ }
122+ }
123+ for & ( trait_def_id, impl_def_id, simplified_self_ty) in & all_trait_impls {
124+ if self . resolver . cstore ( ) . visibility_untracked ( trait_def_id) == Visibility :: Public
125+ && simplified_self_ty. and_then ( |ty| ty. def ( ) ) . map_or ( true , |ty_def_id| {
126+ self . resolver . cstore ( ) . visibility_untracked ( ty_def_id) == Visibility :: Public
127+ } )
128+ {
129+ self . add_traits_in_parent_scope ( impl_def_id) ;
130+ }
131+ }
66132
67133 self . all_traits . extend ( all_traits) ;
68- self . all_trait_impls . extend ( all_trait_impls. into_iter ( ) . map ( |( def_id, _) | def_id) ) ;
134+ self . all_trait_impls . extend ( all_trait_impls. into_iter ( ) . map ( |( _ , def_id, _) | def_id) ) ;
69135 }
70136 }
71137
72138 fn load_links_in_attrs ( & mut self , attrs : & [ ast:: Attribute ] , span : Span ) {
73- // FIXME: this needs to consider export inlining.
139+ // FIXME: this needs to consider reexport inlining.
74140 let attrs = clean:: Attributes :: from_ast ( attrs, None ) ;
75141 for ( parent_module, doc) in attrs. collapsed_doc_value_by_module_level ( ) {
76142 let module_id = parent_module. unwrap_or ( self . current_mod . to_def_id ( ) ) ;
77143
144+ self . add_traits_in_scope ( module_id) ;
145+
78146 for link in markdown_links ( & doc. as_str ( ) ) {
79147 let path_str = if let Some ( Ok ( x) ) = preprocess_link ( & link) {
80148 x. path_str
@@ -85,6 +153,26 @@ impl IntraLinkCrateLoader<'_, '_> {
85153 }
86154 }
87155 }
156+
157+ /// When reexports are inlined, they are replaced with item which they refer to, those items
158+ /// may have links in their doc comments, those links are resolved at the item definition site,
159+ /// so we need to know traits in scope at that definition site.
160+ fn process_module_children_or_reexports ( & mut self , module_id : DefId ) {
161+ if !self . visited_mods . insert ( module_id) {
162+ return ; // avoid infinite recursion
163+ }
164+
165+ for child in self . resolver . module_children_or_reexports ( module_id) {
166+ if child. vis == Visibility :: Public {
167+ if let Some ( def_id) = child. res . opt_def_id ( ) {
168+ self . add_traits_in_parent_scope ( def_id) ;
169+ }
170+ if let Res :: Def ( DefKind :: Mod , module_id) = child. res {
171+ self . process_module_children_or_reexports ( module_id) ;
172+ }
173+ }
174+ }
175+ }
88176}
89177
90178impl Visitor < ' _ > for IntraLinkCrateLoader < ' _ , ' _ > {
@@ -93,6 +181,7 @@ impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
93181 let old_mod = mem:: replace ( & mut self . current_mod , self . resolver . local_def_id ( item. id ) ) ;
94182
95183 self . load_links_in_attrs ( & item. attrs , item. span ) ;
184+ self . process_module_children_or_reexports ( self . current_mod . to_def_id ( ) ) ;
96185 visit:: walk_item ( self , item) ;
97186
98187 self . current_mod = old_mod;
0 commit comments