| //! Allows for producing a unique string key for a mono item. |
| //! These keys are used by the handwritten auto-tests, so they need to be |
| //! predictable and human-readable. |
| //! |
| //! Note: A lot of this could looks very similar to what's already in `ty::print`. |
| //! FIXME(eddyb) implement a custom `PrettyPrinter` for this. |
| |
| use rustc::hir::def_id::DefId; |
| use rustc::ty::subst::SubstsRef; |
| use rustc::ty::{self, Const, Instance, Ty, TyCtxt}; |
| use rustc::{bug, hir}; |
| use std::fmt::Write; |
| use std::iter; |
| |
| /// Same as `unique_type_name()` but with the result pushed onto the given |
| /// `output` parameter. |
| pub struct DefPathBasedNames<'tcx> { |
| tcx: TyCtxt<'tcx>, |
| omit_disambiguators: bool, |
| omit_local_crate_name: bool, |
| } |
| |
| impl DefPathBasedNames<'tcx> { |
| pub fn new(tcx: TyCtxt<'tcx>, omit_disambiguators: bool, omit_local_crate_name: bool) -> Self { |
| DefPathBasedNames { tcx, omit_disambiguators, omit_local_crate_name } |
| } |
| |
| // Pushes the type name of the specified type to the provided string. |
| // If `debug` is true, printing normally unprintable types is allowed |
| // (e.g. `ty::GeneratorWitness`). This parameter should only be set when |
| // this method is being used for logging purposes (e.g. with `debug!` or `info!`) |
| // When being used for codegen purposes, `debug` should be set to `false` |
| // in order to catch unexpected types that should never end up in a type name. |
| pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String, debug: bool) { |
| match t.kind { |
| ty::Bool => output.push_str("bool"), |
| ty::Char => output.push_str("char"), |
| ty::Str => output.push_str("str"), |
| ty::Never => output.push_str("!"), |
| ty::Int(ty) => output.push_str(ty.name_str()), |
| ty::Uint(ty) => output.push_str(ty.name_str()), |
| ty::Float(ty) => output.push_str(ty.name_str()), |
| ty::Adt(adt_def, substs) => { |
| self.push_def_path(adt_def.did, output); |
| self.push_generic_params(substs, iter::empty(), output, debug); |
| } |
| ty::Tuple(component_types) => { |
| output.push('('); |
| for &component_type in component_types { |
| self.push_type_name(component_type.expect_ty(), output, debug); |
| output.push_str(", "); |
| } |
| if !component_types.is_empty() { |
| output.pop(); |
| output.pop(); |
| } |
| output.push(')'); |
| } |
| ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { |
| output.push('*'); |
| match mutbl { |
| hir::Mutability::Not => output.push_str("const "), |
| hir::Mutability::Mut => output.push_str("mut "), |
| } |
| |
| self.push_type_name(inner_type, output, debug); |
| } |
| ty::Ref(_, inner_type, mutbl) => { |
| output.push('&'); |
| output.push_str(mutbl.prefix_str()); |
| |
| self.push_type_name(inner_type, output, debug); |
| } |
| ty::Array(inner_type, len) => { |
| output.push('['); |
| self.push_type_name(inner_type, output, debug); |
| let len = len.eval_usize(self.tcx, ty::ParamEnv::reveal_all()); |
| write!(output, "; {}", len).unwrap(); |
| output.push(']'); |
| } |
| ty::Slice(inner_type) => { |
| output.push('['); |
| self.push_type_name(inner_type, output, debug); |
| output.push(']'); |
| } |
| ty::Dynamic(ref trait_data, ..) => { |
| if let Some(principal) = trait_data.principal() { |
| self.push_def_path(principal.def_id(), output); |
| self.push_generic_params( |
| principal.skip_binder().substs, |
| trait_data.projection_bounds(), |
| output, |
| debug, |
| ); |
| } else { |
| output.push_str("dyn '_"); |
| } |
| } |
| ty::Foreign(did) => self.push_def_path(did, output), |
| ty::FnDef(..) | ty::FnPtr(_) => { |
| let sig = t.fn_sig(self.tcx); |
| output.push_str(sig.unsafety().prefix_str()); |
| |
| let abi = sig.abi(); |
| if abi != ::rustc_target::spec::abi::Abi::Rust { |
| output.push_str("extern \""); |
| output.push_str(abi.name()); |
| output.push_str("\" "); |
| } |
| |
| output.push_str("fn("); |
| |
| let sig = |
| self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); |
| |
| if !sig.inputs().is_empty() { |
| for ¶meter_type in sig.inputs() { |
| self.push_type_name(parameter_type, output, debug); |
| output.push_str(", "); |
| } |
| output.pop(); |
| output.pop(); |
| } |
| |
| if sig.c_variadic { |
| if !sig.inputs().is_empty() { |
| output.push_str(", ..."); |
| } else { |
| output.push_str("..."); |
| } |
| } |
| |
| output.push(')'); |
| |
| if !sig.output().is_unit() { |
| output.push_str(" -> "); |
| self.push_type_name(sig.output(), output, debug); |
| } |
| } |
| ty::Generator(def_id, substs, _) | ty::Closure(def_id, substs) => { |
| self.push_def_path(def_id, output); |
| let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id)); |
| let substs = substs.truncate_to(self.tcx, generics); |
| self.push_generic_params(substs, iter::empty(), output, debug); |
| } |
| ty::Error |
| | ty::Bound(..) |
| | ty::Infer(_) |
| | ty::Placeholder(..) |
| | ty::UnnormalizedProjection(..) |
| | ty::Projection(..) |
| | ty::Param(_) |
| | ty::GeneratorWitness(_) |
| | ty::Opaque(..) => { |
| if debug { |
| output.push_str(&format!("`{:?}`", t)); |
| } else { |
| bug!( |
| "DefPathBasedNames: trying to create type name for unexpected type: {:?}", |
| t, |
| ); |
| } |
| } |
| } |
| } |
| |
| // Pushes the the name of the specified const to the provided string. |
| // If `debug` is true, usually-unprintable consts (such as `Infer`) will be printed, |
| // as well as the unprintable types of constants (see `push_type_name` for more details). |
| pub fn push_const_name(&self, c: &Const<'tcx>, output: &mut String, debug: bool) { |
| if let ty::ConstKind::Value(_) = c.val { |
| // FIXME(const_generics): we could probably do a better job here. |
| write!(output, "{:?}", c).unwrap() |
| } else if debug { |
| write!(output, "{:?}", c).unwrap() |
| } else { |
| bug!("DefPathBasedNames: trying to create const name for unexpected const: {:?}", c,); |
| } |
| output.push_str(": "); |
| self.push_type_name(c.ty, output, debug); |
| } |
| |
| pub fn push_def_path(&self, def_id: DefId, output: &mut String) { |
| let def_path = self.tcx.def_path(def_id); |
| |
| // some_crate:: |
| if !(self.omit_local_crate_name && def_id.is_local()) { |
| output.push_str(&self.tcx.crate_name(def_path.krate).as_str()); |
| output.push_str("::"); |
| } |
| |
| // foo::bar::ItemName:: |
| for part in self.tcx.def_path(def_id).data { |
| if self.omit_disambiguators { |
| write!(output, "{}::", part.data.as_symbol()).unwrap(); |
| } else { |
| write!(output, "{}[{}]::", part.data.as_symbol(), part.disambiguator).unwrap(); |
| } |
| } |
| |
| // remove final "::" |
| output.pop(); |
| output.pop(); |
| } |
| |
| fn push_generic_params<I>( |
| &self, |
| substs: SubstsRef<'tcx>, |
| projections: I, |
| output: &mut String, |
| debug: bool, |
| ) where |
| I: Iterator<Item = ty::PolyExistentialProjection<'tcx>>, |
| { |
| let mut projections = projections.peekable(); |
| if substs.non_erasable_generics().next().is_none() && projections.peek().is_none() { |
| return; |
| } |
| |
| output.push('<'); |
| |
| for type_parameter in substs.types() { |
| self.push_type_name(type_parameter, output, debug); |
| output.push_str(", "); |
| } |
| |
| for projection in projections { |
| let projection = projection.skip_binder(); |
| let name = &self.tcx.associated_item(projection.item_def_id).ident.as_str(); |
| output.push_str(name); |
| output.push_str("="); |
| self.push_type_name(projection.ty, output, debug); |
| output.push_str(", "); |
| } |
| |
| for const_parameter in substs.consts() { |
| self.push_const_name(const_parameter, output, debug); |
| output.push_str(", "); |
| } |
| |
| output.pop(); |
| output.pop(); |
| |
| output.push('>'); |
| } |
| |
| pub fn push_instance_as_string( |
| &self, |
| instance: Instance<'tcx>, |
| output: &mut String, |
| debug: bool, |
| ) { |
| self.push_def_path(instance.def_id(), output); |
| self.push_generic_params(instance.substs, iter::empty(), output, debug); |
| } |
| } |