33//! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
44
55use rustc_ast as ast;
6- use rustc_data_structures:: stable_set:: FxHashSet ;
6+ use rustc_data_structures:: { fx :: FxHashMap , stable_set:: FxHashSet } ;
77use rustc_errors:: { Applicability , DiagnosticBuilder } ;
88use rustc_expand:: base:: SyntaxExtensionKind ;
99use rustc_hir as hir;
@@ -168,6 +168,31 @@ enum AnchorFailure {
168168 RustdocAnchorConflict ( Res ) ,
169169}
170170
171+ #[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
172+ struct CacheKey {
173+ module_id : DefId ,
174+ dis : Option < Disambiguator > ,
175+ path_str : String ,
176+ extra_fragment : Option < String > ,
177+ }
178+
179+ impl CacheKey {
180+ fn new (
181+ module_id : DefId ,
182+ dis : Option < Disambiguator > ,
183+ path_str : String ,
184+ extra_fragment : Option < String > ,
185+ ) -> Self {
186+ Self { module_id, dis, path_str, extra_fragment }
187+ }
188+ }
189+
190+ #[ derive( Clone , Debug , Hash ) ]
191+ struct CachedLink {
192+ pub res : ( Res , Option < String > ) ,
193+ pub side_channel : Option < ( DefKind , DefId ) > ,
194+ }
195+
171196struct LinkCollector < ' a , ' tcx > {
172197 cx : & ' a DocContext < ' tcx > ,
173198 /// A stack of modules used to decide what scope to resolve in.
@@ -179,11 +204,18 @@ struct LinkCollector<'a, 'tcx> {
179204 /// because `clean` and the disambiguator code expect them to be different.
180205 /// See the code for associated items on inherent impls for details.
181206 kind_side_channel : Cell < Option < ( DefKind , DefId ) > > ,
207+ /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link
208+ visited_links : FxHashMap < CacheKey , CachedLink > ,
182209}
183210
184211impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
185212 fn new ( cx : & ' a DocContext < ' tcx > ) -> Self {
186- LinkCollector { cx, mod_ids : Vec :: new ( ) , kind_side_channel : Cell :: new ( None ) }
213+ LinkCollector {
214+ cx,
215+ mod_ids : Vec :: new ( ) ,
216+ kind_side_channel : Cell :: new ( None ) ,
217+ visited_links : FxHashMap :: default ( ) ,
218+ }
187219 }
188220
189221 /// Given a full link, parse it as an [enum struct variant].
@@ -937,7 +969,7 @@ impl LinkCollector<'_, '_> {
937969 ///
938970 /// FIXME(jynelson): this is way too many arguments
939971 fn resolve_link (
940- & self ,
972+ & mut self ,
941973 item : & Item ,
942974 dox : & str ,
943975 self_name : & Option < String > ,
@@ -962,6 +994,7 @@ impl LinkCollector<'_, '_> {
962994 let link = ori_link. replace ( "`" , "" ) ;
963995 let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
964996 let ( link, extra_fragment) = if parts. len ( ) > 2 {
997+ // A valid link can't have multiple #'s
965998 anchor_failure ( cx, & item, & link, dox, link_range, AnchorFailure :: MultipleAnchors ) ;
966999 return None ;
9671000 } else if parts. len ( ) == 2 {
@@ -1075,16 +1108,9 @@ impl LinkCollector<'_, '_> {
10751108 return None ;
10761109 }
10771110
1078- let ( mut res, mut fragment) = self . resolve_with_disambiguator (
1079- disambiguator,
1080- item,
1081- dox,
1082- path_str,
1083- module_id,
1084- extra_fragment,
1085- & ori_link,
1086- link_range. clone ( ) ,
1087- ) ?;
1111+ let key = CacheKey :: new ( module_id, disambiguator, path_str. to_owned ( ) , extra_fragment) ;
1112+ let ( mut res, mut fragment) =
1113+ self . resolve_with_disambiguator_cached ( key, item, dox, & ori_link, link_range. clone ( ) ) ?;
10881114
10891115 // Check for a primitive which might conflict with a module
10901116 // Report the ambiguity and require that the user specify which one they meant.
@@ -1192,6 +1218,45 @@ impl LinkCollector<'_, '_> {
11921218 }
11931219 }
11941220
1221+ fn resolve_with_disambiguator_cached (
1222+ & mut self ,
1223+ key : CacheKey ,
1224+ item : & Item ,
1225+ dox : & str ,
1226+ ori_link : & str ,
1227+ link_range : Option < Range < usize > > ,
1228+ ) -> Option < ( Res , Option < String > ) > {
1229+ // Try to look up both the result and the corresponding side channel value
1230+ if let Some ( ref cached) = self . visited_links . get ( & key) {
1231+ self . kind_side_channel . set ( cached. side_channel . clone ( ) ) ;
1232+ Some ( cached. res . clone ( ) )
1233+ } else {
1234+ match self . resolve_with_disambiguator (
1235+ key. dis ,
1236+ item,
1237+ dox,
1238+ & key. path_str ,
1239+ key. module_id ,
1240+ key. extra_fragment . clone ( ) ,
1241+ ori_link,
1242+ link_range,
1243+ ) {
1244+ Some ( res) => {
1245+ // Store result for the actual namespace
1246+ self . visited_links . insert (
1247+ key,
1248+ CachedLink {
1249+ res : res. clone ( ) ,
1250+ side_channel : self . kind_side_channel . clone ( ) . into_inner ( ) ,
1251+ } ,
1252+ ) ;
1253+ Some ( res)
1254+ }
1255+ _ => None ,
1256+ }
1257+ }
1258+ }
1259+
11951260 /// After parsing the disambiguator, resolve the main part of the link.
11961261 // FIXME(jynelson): wow this is just so much
11971262 fn resolve_with_disambiguator (
@@ -1356,7 +1421,7 @@ impl LinkCollector<'_, '_> {
13561421 }
13571422}
13581423
1359- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
1424+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
13601425/// Disambiguators for a link.
13611426enum Disambiguator {
13621427 /// `prim@`
0 commit comments