@@ -75,6 +75,7 @@ use core::mem;
7575use core:: ops:: ControlFlow ;
7676use std:: collections:: hash_map:: Entry ;
7777use std:: hash:: BuildHasherDefault ;
78+ use std:: iter:: { once, repeat} ;
7879use std:: sync:: { Mutex , MutexGuard , OnceLock } ;
7980
8081use itertools:: Itertools ;
@@ -84,6 +85,7 @@ use rustc_data_structures::packed::Pu128;
8485use rustc_data_structures:: unhash:: UnhashMap ;
8586use rustc_hir:: def:: { DefKind , Res } ;
8687use rustc_hir:: def_id:: { CrateNum , DefId , LocalDefId , LocalModDefId , LOCAL_CRATE } ;
88+ use rustc_hir:: definitions:: { DefPath , DefPathData } ;
8789use rustc_hir:: hir_id:: { HirIdMap , HirIdSet } ;
8890use rustc_hir:: intravisit:: { walk_expr, FnKind , Visitor } ;
8991use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultErr , ResultOk } ;
@@ -102,8 +104,8 @@ use rustc_middle::ty::binding::BindingMode;
102104use rustc_middle:: ty:: fast_reject:: SimplifiedType ;
103105use rustc_middle:: ty:: layout:: IntegerExt ;
104106use 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 ,
107109} ;
108110use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
109111use rustc_span:: source_map:: SourceMap ;
@@ -3264,3 +3266,131 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<
32643266 } )
32653267 }
32663268}
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