@@ -2,11 +2,9 @@ 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 } ;
6- use rustc_hir:: intravisit:: { self , Visitor } ;
7- use rustc_hir:: {
8- ExprKind , HirId , Item , ItemKind , Mod , Node , Pat , PatExpr , PatExprKind , PatKind , QPath ,
9- } ;
5+ use rustc_hir:: def_id:: { DefId , LOCAL_CRATE , LocalDefId } ;
6+ use rustc_hir:: intravisit:: { self , Visitor , VisitorExt } ;
7+ use rustc_hir:: { ExprKind , HirId , Item , ItemKind , Mod , Node , QPath } ;
108use rustc_middle:: hir:: nested_filter;
119use rustc_middle:: ty:: TyCtxt ;
1210use rustc_span:: hygiene:: MacroKind ;
@@ -67,7 +65,7 @@ struct SpanMapVisitor<'tcx> {
6765
6866impl SpanMapVisitor < ' _ > {
6967 /// This function is where we handle `hir::Path` elements and add them into the "span map".
70- fn handle_path ( & mut self , path : & rustc_hir:: Path < ' _ > ) {
68+ fn handle_path ( & mut self , path : & rustc_hir:: Path < ' _ > , only_use_last_segment : bool ) {
7169 match path. res {
7270 // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
7371 // Would be nice to support them too alongside the other `DefKind`
@@ -79,24 +77,36 @@ impl SpanMapVisitor<'_> {
7977 LinkFromSrc :: External ( def_id)
8078 } ;
8179 // In case the path ends with generics, we remove them from the span.
82- let span = path
83- . segments
84- . last ( )
85- . map ( |last| {
86- // In `use` statements, the included item is not in the path segments.
87- // However, it doesn't matter because you can't have generics on `use`
88- // statements.
89- if path. span . contains ( last. ident . span ) {
90- path. span . with_hi ( last. ident . span . hi ( ) )
91- } else {
92- path. span
93- }
94- } )
95- . unwrap_or ( path. span ) ;
80+ let span = if only_use_last_segment
81+ && let Some ( path_span) = path. segments . last ( ) . map ( |segment| segment. ident . span )
82+ {
83+ path_span
84+ } else {
85+ path. segments
86+ . last ( )
87+ . map ( |last| {
88+ // In `use` statements, the included item is not in the path segments.
89+ // However, it doesn't matter because you can't have generics on `use`
90+ // statements.
91+ if path. span . contains ( last. ident . span ) {
92+ path. span . with_hi ( last. ident . span . hi ( ) )
93+ } else {
94+ path. span
95+ }
96+ } )
97+ . unwrap_or ( path. span )
98+ } ;
9699 self . matches . insert ( span, link) ;
97100 }
98101 Res :: Local ( _) if let Some ( span) = self . tcx . hir_res_span ( path. res ) => {
99- self . matches . insert ( path. span , LinkFromSrc :: Local ( clean:: Span :: new ( span) ) ) ;
102+ let path_span = if only_use_last_segment
103+ && let Some ( path_span) = path. segments . last ( ) . map ( |segment| segment. ident . span )
104+ {
105+ path_span
106+ } else {
107+ path. span
108+ } ;
109+ self . matches . insert ( path_span, LinkFromSrc :: Local ( clean:: Span :: new ( span) ) ) ;
100110 }
101111 Res :: PrimTy ( p) => {
102112 // FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
@@ -189,31 +199,23 @@ impl SpanMapVisitor<'_> {
189199 self . matches . insert ( span, link) ;
190200 }
191201 }
202+ }
192203
193- fn handle_pat ( & mut self , p : & Pat < ' _ > ) {
194- let mut check_qpath = |qpath, hir_id| match qpath {
195- QPath :: TypeRelative ( _, path) if matches ! ( path. res, Res :: Err ) => {
196- self . infer_id ( path. hir_id , Some ( hir_id) , qpath. span ( ) ) ;
197- }
198- QPath :: Resolved ( _, path) => self . handle_path ( path) ,
199- _ => { }
200- } ;
201- match p. kind {
202- PatKind :: Binding ( _, _, _, Some ( p) ) => self . handle_pat ( p) ,
203- PatKind :: Struct ( qpath, _, _) | PatKind :: TupleStruct ( qpath, _, _) => {
204- check_qpath ( qpath, p. hir_id )
205- }
206- PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( qpath) , hir_id, .. } ) => {
207- check_qpath ( * qpath, * hir_id)
208- }
209- PatKind :: Or ( pats) => {
210- for pat in pats {
211- self . handle_pat ( pat) ;
212- }
213- }
214- _ => { }
204+ // This is a reimplementation of `hir_enclosing_body_owner` which allows to fail without
205+ // panicking.
206+ fn hir_enclosing_body_owner ( tcx : TyCtxt < ' _ > , hir_id : HirId ) -> Option < LocalDefId > {
207+ for ( _, node) in tcx. hir_parent_iter ( hir_id) {
208+ // FIXME: associated type impl items don't have an associated body, so we don't handle
209+ // them currently.
210+ if let Node :: ImplItem ( impl_item) = node
211+ && matches ! ( impl_item. kind, rustc_hir:: ImplItemKind :: Type ( _) )
212+ {
213+ return None ;
214+ } else if let Some ( ( def_id, _) ) = node. associated_body ( ) {
215+ return Some ( def_id) ;
215216 }
216217 }
218+ None
217219}
218220
219221impl < ' tcx > Visitor < ' tcx > for SpanMapVisitor < ' tcx > {
@@ -227,12 +229,42 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
227229 if self . handle_macro ( path. span ) {
228230 return ;
229231 }
230- self . handle_path ( path) ;
232+ self . handle_path ( path, false ) ;
231233 intravisit:: walk_path ( self , path) ;
232234 }
233235
234- fn visit_pat ( & mut self , p : & Pat < ' tcx > ) {
235- self . handle_pat ( p) ;
236+ fn visit_qpath ( & mut self , qpath : & QPath < ' tcx > , id : HirId , _span : Span ) {
237+ match * qpath {
238+ QPath :: TypeRelative ( qself, path) => {
239+ if matches ! ( path. res, Res :: Err ) {
240+ let tcx = self . tcx ;
241+ if let Some ( body_id) = hir_enclosing_body_owner ( tcx, id) {
242+ let typeck_results = tcx. typeck_body ( tcx. hir_body_owned_by ( body_id) . id ( ) ) ;
243+ let path = rustc_hir:: Path {
244+ // We change the span to not include parens.
245+ span : path. ident . span ,
246+ res : typeck_results. qpath_res ( qpath, id) ,
247+ segments : & [ ] ,
248+ } ;
249+ self . handle_path ( & path, false ) ;
250+ }
251+ } else {
252+ self . infer_id ( path. hir_id , Some ( id) , path. ident . span ) ;
253+ }
254+
255+ rustc_ast:: visit:: try_visit!( self . visit_ty_unambig( qself) ) ;
256+ self . visit_path_segment ( path) ;
257+ }
258+ QPath :: Resolved ( maybe_qself, path) => {
259+ self . handle_path ( path, true ) ;
260+
261+ rustc_ast:: visit:: visit_opt!( self , visit_ty_unambig, maybe_qself) ;
262+ if !self . handle_macro ( path. span ) {
263+ intravisit:: walk_path ( self , path) ;
264+ }
265+ }
266+ _ => { }
267+ }
236268 }
237269
238270 fn visit_mod ( & mut self , m : & ' tcx Mod < ' tcx > , span : Span , id : HirId ) {
0 commit comments