use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::DefId; use rustc_span::Span; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; use crate::ty::{self, Ty, TyCtxt}; /// An additional filter to exclude well-known types from the ecosystem /// because their drops are trivial. /// This returns additional types to check if the drops are delegated to those. /// A typical example is `hashbrown::HashMap`, whose drop is delegated to `K` and `V`. fn true_significant_drop_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option; 2]>> { if let ty::Adt(def, args) = ty.kind() { let mut did = def.did(); let mut name_rev = vec![]; loop { let key = tcx.def_key(did); match key.disambiguated_data.data { rustc_hir::definitions::DefPathData::CrateRoot => { name_rev.push(tcx.crate_name(did.krate)); } rustc_hir::definitions::DefPathData::TypeNs(symbol) => { name_rev.push(symbol); } _ => return None, } if let Some(parent) = key.parent { did = DefId { krate: did.krate, index: parent }; } else { break; } } let name_str: Vec<_> = name_rev.iter().rev().map(|x| x.as_str()).collect(); debug!(?name_str); match name_str[..] { // These are the types from Rust core ecosystem ["syn" | "proc_macro2", ..] | ["core" | "std", "task", "LocalWaker" | "Waker"] | ["core" | "std", "task", "wake", "LocalWaker" | "Waker"] => Some(smallvec![]), // These are important types from Rust ecosystem ["tracing", "instrument", "Instrumented"] | ["bytes", "Bytes"] => Some(smallvec![]), ["hashbrown", "raw", "RawTable" | "RawIntoIter"] => { if let [ty, ..] = &***args && let Some(ty) = ty.as_type() { Some(smallvec![ty]) } else { None } } ["hashbrown", "raw", "RawDrain"] => { if let [_, ty, ..] = &***args && let Some(ty) = ty.as_type() { Some(smallvec![ty]) } else { None } } _ => None, } } else { None } } /// Returns the list of types with a "potentially significant" that may be dropped /// by dropping a value of type `ty`. #[instrument(level = "trace", skip(tcx, typing_env))] pub fn extract_component_raw<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, ty_seen: &mut UnordSet>, ) -> SmallVec<[Ty<'tcx>; 4]> { // Droppiness does not depend on regions, so let us erase them. let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); let tys = tcx.list_significant_drop_tys(typing_env.as_query_input(ty)); debug!(?ty, "components"); let mut out_tys = smallvec![]; for ty in tys { if let Some(tys) = true_significant_drop_ty(tcx, ty) { // Some types can be further opened up because the drop is simply delegated for ty in tys { if ty_seen.insert(ty) { out_tys.extend(extract_component_raw(tcx, typing_env, ty, ty_seen)); } } } else { if ty_seen.insert(ty) { out_tys.push(ty); } } } out_tys } #[instrument(level = "trace", skip(tcx, typing_env))] pub fn extract_component_with_significant_dtor<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, ) -> SmallVec<[Ty<'tcx>; 4]> { let mut tys = extract_component_raw(tcx, typing_env, ty, &mut Default::default()); let mut deduplicate = FxHashSet::default(); tys.retain(|oty| deduplicate.insert(*oty)); tys.into_iter().collect() } /// Extract the span of the custom destructor of a type /// especially the span of the `impl Drop` header or its entire block /// when we are working with current local crate. #[instrument(level = "trace", skip(tcx))] pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Error(_) | ty::Str | ty::Never | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnPtr(_, _) | ty::Tuple(_) | ty::Dynamic(_, _) | ty::Alias(_, _) | ty::Bound(_, _) | ty::Pat(_, _) | ty::Placeholder(_) | ty::Infer(_) | ty::Slice(_) | ty::Array(_, _) | ty::UnsafeBinder(_) => None, ty::Adt(adt_def, _) => { if let Some(dtor) = tcx.adt_destructor(adt_def.did()) { Some(tcx.def_span(tcx.parent(dtor.did))) } else { Some(tcx.def_span(adt_def.did())) } } ty::Coroutine(did, _) | ty::CoroutineWitness(did, _) | ty::CoroutineClosure(did, _) | ty::Closure(did, _) | ty::FnDef(did, _) | ty::Foreign(did) => Some(tcx.def_span(did)), ty::Param(_) => None, } }