@@ -2,11 +2,9 @@ use std::path::{Path, PathBuf};
2
2
3
3
use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap } ;
4
4
use 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 } ;
10
8
use rustc_middle:: hir:: nested_filter;
11
9
use rustc_middle:: ty:: TyCtxt ;
12
10
use rustc_span:: hygiene:: MacroKind ;
@@ -67,7 +65,7 @@ struct SpanMapVisitor<'tcx> {
67
65
68
66
impl SpanMapVisitor < ' _ > {
69
67
/// 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 ) {
71
69
match path. res {
72
70
// FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
73
71
// Would be nice to support them too alongside the other `DefKind`
@@ -79,24 +77,36 @@ impl SpanMapVisitor<'_> {
79
77
LinkFromSrc :: External ( def_id)
80
78
} ;
81
79
// 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
+ } ;
96
99
self . matches . insert ( span, link) ;
97
100
}
98
101
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) ) ) ;
100
110
}
101
111
Res :: PrimTy ( p) => {
102
112
// FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
@@ -189,31 +199,23 @@ impl SpanMapVisitor<'_> {
189
199
self . matches . insert ( span, link) ;
190
200
}
191
201
}
202
+ }
192
203
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) ;
215
216
}
216
217
}
218
+ None
217
219
}
218
220
219
221
impl < ' tcx > Visitor < ' tcx > for SpanMapVisitor < ' tcx > {
@@ -227,12 +229,42 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
227
229
if self . handle_macro ( path. span ) {
228
230
return ;
229
231
}
230
- self . handle_path ( path) ;
232
+ self . handle_path ( path, false ) ;
231
233
intravisit:: walk_path ( self , path) ;
232
234
}
233
235
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
+ }
236
268
}
237
269
238
270
fn visit_mod ( & mut self , m : & ' tcx Mod < ' tcx > , span : Span , id : HirId ) {
0 commit comments