@@ -75,6 +75,7 @@ use core::mem;
75
75
use core:: ops:: ControlFlow ;
76
76
use std:: collections:: hash_map:: Entry ;
77
77
use std:: hash:: BuildHasherDefault ;
78
+ use std:: iter:: { once, repeat} ;
78
79
use std:: sync:: { Mutex , MutexGuard , OnceLock } ;
79
80
80
81
use itertools:: Itertools ;
@@ -84,6 +85,7 @@ use rustc_data_structures::packed::Pu128;
84
85
use rustc_data_structures:: unhash:: UnhashMap ;
85
86
use rustc_hir:: def:: { DefKind , Res } ;
86
87
use rustc_hir:: def_id:: { CrateNum , DefId , LocalDefId , LocalModDefId , LOCAL_CRATE } ;
88
+ use rustc_hir:: definitions:: { DefPath , DefPathData } ;
87
89
use rustc_hir:: hir_id:: { HirIdMap , HirIdSet } ;
88
90
use rustc_hir:: intravisit:: { walk_expr, FnKind , Visitor } ;
89
91
use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultErr , ResultOk } ;
@@ -102,8 +104,8 @@ use rustc_middle::ty::binding::BindingMode;
102
104
use rustc_middle:: ty:: fast_reject:: SimplifiedType ;
103
105
use rustc_middle:: ty:: layout:: IntegerExt ;
104
106
use rustc_middle:: ty:: {
105
- self as rustc_ty, Binder , BorrowKind , ClosureKind , FloatTy , IntTy , ParamEnv , ParamEnvAnd , Ty , TyCtxt , TypeAndMut ,
106
- TypeVisitableExt , UintTy , UpvarCapture ,
107
+ self as rustc_ty, Binder , BorrowKind , ClosureKind , EarlyBinder , FloatTy , GenericArgsRef , IntTy , ParamEnv ,
108
+ ParamEnvAnd , Ty , TyCtxt , TypeAndMut , TypeVisitableExt , UintTy , UpvarCapture ,
107
109
} ;
108
110
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
109
111
use rustc_span:: source_map:: SourceMap ;
@@ -3264,3 +3266,131 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<
3264
3266
} )
3265
3267
}
3266
3268
}
3269
+
3270
+ /// Produces a path from a local caller to the type of the called method. Suitable for user
3271
+ /// output/suggestions.
3272
+ ///
3273
+ /// Returned path can be either absolute (for methods defined non-locally), or relative (for local
3274
+ /// methods).
3275
+ pub fn get_path_from_caller_to_method_type < ' tcx > (
3276
+ tcx : TyCtxt < ' tcx > ,
3277
+ from : LocalDefId ,
3278
+ method : DefId ,
3279
+ args : GenericArgsRef < ' tcx > ,
3280
+ ) -> String {
3281
+ let assoc_item = tcx. associated_item ( method) ;
3282
+ let def_id = assoc_item. container_id ( tcx) ;
3283
+ match assoc_item. container {
3284
+ rustc_ty:: TraitContainer => get_path_to_callee ( tcx, from, def_id) ,
3285
+ rustc_ty:: ImplContainer => {
3286
+ let ty = tcx. type_of ( def_id) . instantiate_identity ( ) ;
3287
+ get_path_to_ty ( tcx, from, ty, args)
3288
+ } ,
3289
+ }
3290
+ }
3291
+
3292
+ fn get_path_to_ty < ' tcx > ( tcx : TyCtxt < ' tcx > , from : LocalDefId , ty : Ty < ' tcx > , args : GenericArgsRef < ' tcx > ) -> String {
3293
+ match ty. kind ( ) {
3294
+ rustc_ty:: Adt ( adt, _) => get_path_to_callee ( tcx, from, adt. did ( ) ) ,
3295
+ // TODO these types need to be recursively resolved as well
3296
+ rustc_ty:: Array ( ..)
3297
+ | rustc_ty:: Dynamic ( ..)
3298
+ | rustc_ty:: Never
3299
+ | rustc_ty:: RawPtr ( _)
3300
+ | rustc_ty:: Ref ( ..)
3301
+ | rustc_ty:: Slice ( _)
3302
+ | rustc_ty:: Tuple ( _) => format ! ( "<{}>" , EarlyBinder :: bind( ty) . instantiate( tcx, args) ) ,
3303
+ _ => ty. to_string ( ) ,
3304
+ }
3305
+ }
3306
+
3307
+ /// Produce a path from some local caller to the callee. Suitable for user output/suggestions.
3308
+ fn get_path_to_callee ( tcx : TyCtxt < ' _ > , from : LocalDefId , callee : DefId ) -> String {
3309
+ // only search for a relative path if the call is fully local
3310
+ if callee. is_local ( ) {
3311
+ let callee_path = tcx. def_path ( callee) ;
3312
+ let caller_path = tcx. def_path ( from. to_def_id ( ) ) ;
3313
+ maybe_get_relative_path ( & caller_path, & callee_path, 2 )
3314
+ } else {
3315
+ tcx. def_path_str ( callee)
3316
+ }
3317
+ }
3318
+
3319
+ /// Tries to produce a relative path from `from` to `to`; if such a path would contain more than
3320
+ /// `max_super` `super` items, produces an absolute path instead. Both `from` and `to` should be in
3321
+ /// the local crate.
3322
+ ///
3323
+ /// Suitable for user output/suggestions.
3324
+ ///
3325
+ /// This ignores use items, and assumes that the target path is visible from the source
3326
+ /// path (which _should_ be a reasonable assumption since we in order to be able to use an object of
3327
+ /// certain type T, T is required to be visible).
3328
+ ///
3329
+ /// TODO make use of `use` items. Maybe we should have something more sophisticated like
3330
+ /// rust-analyzer does? <https://docs.rs/ra_ap_hir_def/0.0.169/src/ra_ap_hir_def/find_path.rs.html#19-27>
3331
+ fn maybe_get_relative_path ( from : & DefPath , to : & DefPath , max_super : usize ) -> String {
3332
+ use itertools:: EitherOrBoth :: { Both , Left , Right } ;
3333
+
3334
+ // 1. skip the segments common for both paths (regardless of their type)
3335
+ let unique_parts = to
3336
+ . data
3337
+ . iter ( )
3338
+ . zip_longest ( from. data . iter ( ) )
3339
+ . skip_while ( |el| matches ! ( el, Both ( l, r) if l == r) )
3340
+ . map ( |el| match el {
3341
+ Both ( l, r) => Both ( l. data , r. data ) ,
3342
+ Left ( l) => Left ( l. data ) ,
3343
+ Right ( r) => Right ( r. data ) ,
3344
+ } ) ;
3345
+
3346
+ // 2. for the remaning segments, construct relative path using only mod names and `super`
3347
+ let mut go_up_by = 0 ;
3348
+ let mut path = Vec :: new ( ) ;
3349
+ for el in unique_parts {
3350
+ match el {
3351
+ Both ( l, r) => {
3352
+ // consider:
3353
+ // a::b::sym:: :: refers to
3354
+ // c::d::e ::f::sym
3355
+ // result should be super::super::c::d::e::f
3356
+ //
3357
+ // alternatively:
3358
+ // a::b::c ::d::sym refers to
3359
+ // e::f::sym:: ::
3360
+ // result should be super::super::super::super::e::f
3361
+ if let DefPathData :: TypeNs ( s) = l {
3362
+ path. push ( s. to_string ( ) ) ;
3363
+ }
3364
+ if let DefPathData :: TypeNs ( _) = r {
3365
+ go_up_by += 1 ;
3366
+ }
3367
+ } ,
3368
+ // consider:
3369
+ // a::b::sym:: :: refers to
3370
+ // c::d::e ::f::sym
3371
+ // when looking at `f`
3372
+ Left ( DefPathData :: TypeNs ( sym) ) => path. push ( sym. to_string ( ) ) ,
3373
+ // consider:
3374
+ // a::b::c ::d::sym refers to
3375
+ // e::f::sym:: ::
3376
+ // when looking at `d`
3377
+ Right ( DefPathData :: TypeNs ( _) ) => go_up_by += 1 ,
3378
+ _ => { } ,
3379
+ }
3380
+ }
3381
+
3382
+ if go_up_by > max_super {
3383
+ // `super` chain would be too long, just use the absolute path instead
3384
+ once ( String :: from ( "crate" ) )
3385
+ . chain ( to. data . iter ( ) . filter_map ( |el| {
3386
+ if let DefPathData :: TypeNs ( sym) = el. data {
3387
+ Some ( sym. to_string ( ) )
3388
+ } else {
3389
+ None
3390
+ }
3391
+ } ) )
3392
+ . join ( "::" )
3393
+ } else {
3394
+ repeat ( String :: from ( "super" ) ) . take ( go_up_by) . chain ( path) . join ( "::" )
3395
+ }
3396
+ }
0 commit comments