@@ -2,11 +2,12 @@ use std::path::{Path, PathBuf};
22
33use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap } ;
44use rustc_hir:: def:: { DefKind , Res } ;
5- use rustc_hir:: def_id:: { DefId , LOCAL_CRATE , LocalDefId } ;
5+ use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
66use rustc_hir:: intravisit:: { self , Visitor , VisitorExt } ;
77use rustc_hir:: { ExprKind , HirId , Item , ItemKind , Mod , Node , QPath } ;
8+ use rustc_hir as hir;
89use rustc_middle:: hir:: nested_filter;
9- use rustc_middle:: ty:: TyCtxt ;
10+ use rustc_middle:: ty:: { self , TyCtxt } ;
1011use rustc_span:: { BytePos , ExpnKind } ;
1112
1213use crate :: clean:: { self , PrimitiveType , rustc_span} ;
@@ -82,7 +83,7 @@ pub(crate) fn collect_spans_and_sources(
8283 generate_link_to_definition : bool ,
8384) -> ( FxIndexMap < PathBuf , String > , FxHashMap < Span , LinkFromSrc > ) {
8485 if include_sources {
85- let mut visitor = SpanMapVisitor { tcx, matches : FxHashMap :: default ( ) } ;
86+ let mut visitor = SpanMapVisitor { tcx, cached_typeck_results : None , matches : FxHashMap :: default ( ) } ;
8687
8788 if generate_link_to_definition {
8889 tcx. hir_walk_toplevel_module ( & mut visitor) ;
@@ -96,12 +97,24 @@ pub(crate) fn collect_spans_and_sources(
9697
9798struct SpanMapVisitor < ' tcx > {
9899 pub ( crate ) tcx : TyCtxt < ' tcx > ,
100+ pub ( crate ) cached_typeck_results : Option < ( hir:: BodyId , Option < & ' tcx ty:: TypeckResults < ' tcx > > ) > ,
99101 pub ( crate ) matches : FxHashMap < Span , LinkFromSrc > ,
100102}
101103
102- impl SpanMapVisitor < ' _ > {
104+ impl < ' tcx > SpanMapVisitor < ' tcx > {
105+ fn maybe_typeck_results ( & mut self ) -> Option < & ' tcx ty:: TypeckResults < ' tcx > > {
106+ let ( body_id, cached_typeck_results) = self . cached_typeck_results . as_mut ( ) ?;
107+
108+ //FIXME: Allegedly, we might actually typeck code here that's cfg'ed out
109+ // which could lead to errors getting reported which would be wild.
110+ // Re-audit this! Do we have any tests demonstrating this?
111+ // And certainly don't silence diagnostics in the compiler. Historical context:
112+ // <https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352>.
113+ Some ( cached_typeck_results. get_or_insert_with ( || self . tcx . typeck_body ( * body_id) ) )
114+ }
115+
103116 /// This function is where we handle `hir::Path` elements and add them into the "span map".
104- fn handle_path ( & mut self , path : & rustc_hir :: Path < ' _ > , only_use_last_segment : bool ) {
117+ fn handle_path ( & mut self , path : & hir :: Path < ' _ > , only_use_last_segment : bool ) {
105118 match path. res {
106119 // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
107120 // Would be nice to support them too alongside the other `DefKind`
@@ -217,19 +230,16 @@ impl SpanMapVisitor<'_> {
217230 true
218231 }
219232
233+ // FIXME: Find a better name for this? One that's more specific?
234+ // Or inline this function into all callers and create better helpers?
220235 fn infer_id ( & mut self , hir_id : HirId , expr_hir_id : Option < HirId > , span : Span ) {
221- let tcx = self . tcx ;
222- let body_id = tcx. hir_enclosing_body_owner ( hir_id) ;
223- // FIXME: this is showing error messages for parts of the code that are not
224- // compiled (because of cfg)!
225- //
226- // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
227- let typeck_results = tcx. typeck_body ( tcx. hir_body_owned_by ( body_id) . id ( ) ) ;
236+ let Some ( typeck_results) = self . maybe_typeck_results ( ) else { return } ;
237+
228238 // Interestingly enough, for method calls, we need the whole expression whereas for static
229239 // method/function calls, we need the call expression specifically.
230240 if let Some ( def_id) = typeck_results. type_dependent_def_id ( expr_hir_id. unwrap_or ( hir_id) ) {
231241 let link = if def_id. as_local ( ) . is_some ( ) {
232- LinkFromSrc :: Local ( rustc_span ( def_id, tcx) )
242+ LinkFromSrc :: Local ( rustc_span ( def_id, self . tcx ) )
233243 } else {
234244 LinkFromSrc :: External ( def_id)
235245 } ;
@@ -238,31 +248,20 @@ impl SpanMapVisitor<'_> {
238248 }
239249}
240250
241- // This is a reimplementation of `hir_enclosing_body_owner` which allows to fail without
242- // panicking.
243- fn hir_enclosing_body_owner ( tcx : TyCtxt < ' _ > , hir_id : HirId ) -> Option < LocalDefId > {
244- for ( _, node) in tcx. hir_parent_iter ( hir_id) {
245- // FIXME: associated type impl items don't have an associated body, so we don't handle
246- // them currently.
247- if let Node :: ImplItem ( impl_item) = node
248- && matches ! ( impl_item. kind, rustc_hir:: ImplItemKind :: Type ( _) )
249- {
250- return None ;
251- } else if let Some ( ( def_id, _) ) = node. associated_body ( ) {
252- return Some ( def_id) ;
253- }
254- }
255- None
256- }
257-
258251impl < ' tcx > Visitor < ' tcx > for SpanMapVisitor < ' tcx > {
259252 type NestedFilter = nested_filter:: All ;
260253
261254 fn maybe_tcx ( & mut self ) -> Self :: MaybeTyCtxt {
262255 self . tcx
263256 }
264257
265- fn visit_path ( & mut self , path : & rustc_hir:: Path < ' tcx > , _id : HirId ) {
258+ fn visit_nested_body ( & mut self , body_id : hir:: BodyId ) -> Self :: Result {
259+ let cached_typeck_results = self . cached_typeck_results . replace ( ( body_id, None ) ) ;
260+ self . visit_body ( self . tcx . hir_body ( body_id) ) ;
261+ self . cached_typeck_results = cached_typeck_results;
262+ }
263+
264+ fn visit_path ( & mut self , path : & hir:: Path < ' tcx > , _id : HirId ) {
266265 if self . handle_macro ( path. span ) {
267266 return ;
268267 }
@@ -272,25 +271,28 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
272271
273272 fn visit_qpath ( & mut self , qpath : & QPath < ' tcx > , id : HirId , _span : rustc_span:: Span ) {
274273 match * qpath {
275- QPath :: TypeRelative ( qself, path ) => {
276- if matches ! ( path . res , Res :: Err ) {
277- let tcx = self . tcx ;
278- if let Some ( body_id ) = hir_enclosing_body_owner ( tcx , id ) {
279- let typeck_results = tcx . typeck_body ( tcx . hir_body_owned_by ( body_id ) . id ( ) ) ;
280- let path = rustc_hir :: Path {
274+ QPath :: TypeRelative ( qself, segment ) => {
275+ if let Res :: Err = segment . res {
276+ if let Some ( typeck_results ) = self . maybe_typeck_results ( )
277+ && typeck_results . hir_owner == id . owner
278+ {
279+ let path = hir :: Path {
281280 // We change the span to not include parens.
282- span : path . ident . span ,
281+ span : segment . ident . span ,
283282 res : typeck_results. qpath_res ( qpath, id) ,
283+ // FIXME: Don't create a path with zero segments!
284+ // Can't we just pass `std::slice::from_ref(segment)` or
285+ // would that affect the behavior of `handle_path`?
284286 segments : & [ ] ,
285287 } ;
286288 self . handle_path ( & path, false ) ;
287289 }
288290 } else {
289- self . infer_id ( path . hir_id , Some ( id) , path . ident . span . into ( ) ) ;
291+ self . infer_id ( segment . hir_id , Some ( id) , segment . ident . span . into ( ) ) ;
290292 }
291293
292294 rustc_ast:: visit:: try_visit!( self . visit_ty_unambig( qself) ) ;
293- self . visit_path_segment ( path ) ;
295+ self . visit_path_segment ( segment ) ;
294296 }
295297 QPath :: Resolved ( maybe_qself, path) => {
296298 self . handle_path ( path, true ) ;
@@ -323,11 +325,15 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
323325 intravisit:: walk_mod ( self , m) ;
324326 }
325327
326- fn visit_expr ( & mut self , expr : & ' tcx rustc_hir :: Expr < ' tcx > ) {
328+ fn visit_expr ( & mut self , expr : & ' tcx hir :: Expr < ' tcx > ) {
327329 match expr. kind {
328330 ExprKind :: MethodCall ( segment, ..) => {
329331 self . infer_id ( segment. hir_id , Some ( expr. hir_id ) , segment. ident . span . into ( ) )
330332 }
333+ // FIXME: Don't we register matches twice if `call` was a `TypeRelative` path?
334+ // I don't think we can just cease walking `call` since then generic args
335+ // inside the path won't get resolved I'm pretty sure.
336+ // Double-resolving probably doesn't matter though anyway wrt. perf.
331337 ExprKind :: Call ( call, ..) => self . infer_id ( call. hir_id , None , call. span . into ( ) ) ,
332338 _ => {
333339 if self . handle_macro ( expr. span ) {
0 commit comments