1//! The `HirDisplay` trait, which serves two purposes: Turning various bits from2//! HIR back into source code, and just displaying them for debugging/testing3//! purposes.45use std::{6 fmt::{self, Debug},7 mem,8};910use base_db::{Crate, FxIndexMap};11use either::Either;12use hir_def::{13 ExpressionStoreOwnerId, FindPathConfig, GenericDefId, GenericParamId, HasModule,14 ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, TypeAliasId,15 expr_store::{ExpressionStore, path::Path},16 find_path::{self, PrefixKind},17 hir::{18 ClosureKind as HirClosureKind, CoroutineKind, PatId,19 generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate},20 },21 item_scope::ItemInNs,22 item_tree::FieldsShape,23 lang_item::LangItems,24 signatures::{25 ConstSignature, EnumSignature, FunctionSignature, StaticSignature, StructSignature,26 TraitSignature, TypeAliasSignature, UnionSignature, VariantFields,27 },28 type_ref::{29 ConstRef, LifetimeRef, LifetimeRefId, TraitBoundModifier, TypeBound, TypeRef, TypeRefId,30 UseArgRef,31 },32 visibility::Visibility,33};34use hir_expand::{mod_path::PathKind, name::Name};35use intern::{Internable, Interned, sym};36use itertools::Itertools;37use la_arena::ArenaMap;38use rustc_abi::ExternAbi;39use rustc_apfloat::{40 Float,41 ieee::{Half as f16, Quad as f128},42};43use rustc_ast_ir::FloatTy;44use rustc_hash::FxHashSet;45use rustc_type_ir::{46 AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast,47 inherent::{GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _},48};49use smallvec::SmallVec;50use span::Edition;51use stdx::never;5253use crate::{54 CallableDefId, FieldType, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval,55 db::{GeneralConstId, HirDatabase},56 generics::{ProvenanceSplit, generics},57 layout::Layout,58 lower::GenericPredicates,59 mir::pad16,60 next_solver::{61 AliasTy, Allocation, Clause, ClauseKind, Const, ConstKind, DbInterner,62 ExistentialPredicate, FnSig, GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig,63 Region, Term, TermId, TermKind, TraitPredicate, TraitRef, Ty, TyKind, TypingMode,64 Unnormalized, ValTree,65 abi::Safety,66 infer::{DbInternerInferExt, traits::ObligationCause},67 },68 primitive,69 utils::{detect_variant_from_bytes, fn_traits},70};7172fn async_gen_item_ty_from_yield_ty<'db>(73 lang_items: &LangItems,74 yield_ty: Ty<'db>,75) -> Option<Ty<'db>> {76 let poll_id = lang_items.Poll.map(hir_def::AdtId::EnumId)?;77 let option_id = lang_items.Option.map(hir_def::AdtId::EnumId)?;7879 let TyKind::Adt(poll_def, poll_args) = yield_ty.kind() else {80 return None;81 };82 if poll_def.def_id() != poll_id {83 return None;84 }85 let [poll_inner] = poll_args.as_slice() else {86 return None;87 };88 let poll_inner = poll_inner.ty()?;8990 let TyKind::Adt(option_def, option_args) = poll_inner.kind() else {91 return None;92 };93 if option_def.def_id() != option_id {94 return None;95 }96 let [item] = option_args.as_slice() else {97 return None;98 };99 item.ty()100}101102pub type Result<T = (), E = HirDisplayError> = std::result::Result<T, E>;103104pub trait HirWrite: fmt::Write {105 fn start_location_link(&mut self, _location: ModuleDefId) {}106 fn start_location_link_generic(&mut self, _location: GenericParamId) {}107 fn end_location_link(&mut self) {}108}109110// String will ignore link metadata111impl HirWrite for String {}112113// `core::Formatter` will ignore metadata114impl HirWrite for fmt::Formatter<'_> {}115116pub struct HirFormatter<'a, 'db> {117 /// The database handle118 pub db: &'db dyn HirDatabase,119 pub interner: DbInterner<'db>,120 /// The sink to write into121 fmt: &'a mut dyn HirWrite,122 /// A buffer to intercept writes with, this allows us to track the overall size of the formatted output.123 buf: String,124 /// The current size of the formatted output.125 curr_size: usize,126 /// Size from which we should truncate the output.127 max_size: Option<usize>,128 /// When rendering something that has a concept of "children" (like fields in a struct), this limits129 /// how many should be rendered.130 pub entity_limit: Option<usize>,131 /// When rendering functions, whether to show the constraint from the container132 show_container_bounds: bool,133 omit_verbose_types: bool,134 closure_style: ClosureStyle,135 display_lifetimes: DisplayLifetime,136 display_kind: DisplayKind,137 display_target: DisplayTarget,138 /// We can have recursive bounds like the following case:139 /// ```ignore140 /// where141 /// T: Foo,142 /// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar143 /// ```144 /// So, record the projection types met while formatting bounds and145 /// prevent recursing into their bounds to avoid infinite loops.146 currently_formatting_bounds: FxHashSet<AliasTy<'db>>,147 /// Whether formatting `impl Trait1 + Trait2` or `dyn Trait1 + Trait2` needs parentheses around it,148 /// for example when formatting `&(impl Trait1 + Trait2)`.149 trait_bounds_need_parens: bool,150}151152// FIXME: To consider, ref and dyn trait lifetimes can be omitted if they are `'_`, path args should153// not be when in signatures154// So this enum does not encode this well enough155// Also 'static can be omitted for ref and dyn trait lifetimes in static/const item types156// FIXME: Also named lifetimes may be rendered in places where their name is not in scope?157#[derive(Copy, Clone)]158pub enum DisplayLifetime {159 Always,160 OnlyStatic,161 OnlyNamed,162 OnlyNamedOrStatic,163 Never,164}165166impl<'db> HirFormatter<'_, 'db> {167 pub fn start_location_link(&mut self, location: ModuleDefId) {168 self.fmt.start_location_link(location);169 }170171 pub fn start_location_link_generic(&mut self, location: GenericParamId) {172 self.fmt.start_location_link_generic(location);173 }174175 pub fn end_location_link(&mut self) {176 self.fmt.end_location_link();177 }178179 fn format_bounds_with<F: FnOnce(&mut Self) -> Result>(180 &mut self,181 target: AliasTy<'db>,182 format_bounds: F,183 ) -> Result {184 if self.currently_formatting_bounds.insert(target) {185 let result = format_bounds(self);186 self.currently_formatting_bounds.remove(&target);187 result188 } else {189 if self.display_kind.is_source_code() {190 Err(HirDisplayError::DisplaySourceCodeError(DisplaySourceCodeError::Cycle))191 } else {192 match target.kind {193 AliasTyKind::Projection { def_id } => {194 let def_id = def_id.0;195 let ItemContainerId::TraitId(trait_) = def_id.loc(self.db).container else {196 panic!("expected an assoc type");197 };198 let trait_name = &TraitSignature::of(self.db, trait_).name;199 let assoc_type_name = &TypeAliasSignature::of(self.db, def_id).name;200 write!(201 self,202 "<… as {}>::{}",203 trait_name.display(self.db, self.edition()),204 assoc_type_name.display(self.db, self.edition()),205 )?;206 if target.args.len() > 1 {207 self.write_str("<…>")?;208 }209 Ok(())210 }211 AliasTyKind::Inherent { .. }212 | AliasTyKind::Opaque { .. }213 | AliasTyKind::Free { .. } => self.write_str("…"),214 }215 }216 }217 }218219 fn render_region(&self, lifetime: Region<'db>) -> bool {220 match self.display_lifetimes {221 DisplayLifetime::Always => true,222 DisplayLifetime::OnlyStatic => matches!(lifetime.kind(), RegionKind::ReStatic),223 DisplayLifetime::OnlyNamed => {224 matches!(lifetime.kind(), RegionKind::ReEarlyParam(_))225 }226 DisplayLifetime::OnlyNamedOrStatic => {227 matches!(lifetime.kind(), RegionKind::ReStatic | RegionKind::ReEarlyParam(_))228 }229 DisplayLifetime::Never => false,230 }231 }232}233234pub trait HirDisplay<'db> {235 fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result;236237 /// Returns a `Display`able type that is human-readable.238 fn into_displayable<'a>(239 &'a self,240 db: &'db dyn HirDatabase,241 max_size: Option<usize>,242 limited_size: Option<usize>,243 omit_verbose_types: bool,244 display_target: DisplayTarget,245 display_kind: DisplayKind,246 closure_style: ClosureStyle,247 show_container_bounds: bool,248 ) -> HirDisplayWrapper<'a, 'db, Self>249 where250 Self: Sized,251 {252 assert!(253 !matches!(display_kind, DisplayKind::SourceCode { .. }),254 "HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"255 );256 HirDisplayWrapper {257 db,258 t: self,259 max_size,260 limited_size,261 omit_verbose_types,262 display_target,263 display_kind,264 closure_style,265 show_container_bounds,266 display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,267 }268 }269270 /// Returns a `Display`able type that is human-readable.271 /// Use this for showing types to the user (e.g. diagnostics)272 fn display<'a>(273 &'a self,274 db: &'db dyn HirDatabase,275 display_target: DisplayTarget,276 ) -> HirDisplayWrapper<'a, 'db, Self>277 where278 Self: Sized,279 {280 HirDisplayWrapper {281 db,282 t: self,283 max_size: None,284 limited_size: None,285 omit_verbose_types: false,286 closure_style: ClosureStyle::ImplFn,287 display_target,288 display_kind: DisplayKind::Diagnostics,289 show_container_bounds: false,290 display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,291 }292 }293294 /// Returns a `Display`able type that is human-readable and tries to be succinct.295 /// Use this for showing types to the user where space is constrained (e.g. doc popups)296 fn display_truncated<'a>(297 &'a self,298 db: &'db dyn HirDatabase,299 max_size: Option<usize>,300 display_target: DisplayTarget,301 ) -> HirDisplayWrapper<'a, 'db, Self>302 where303 Self: Sized,304 {305 HirDisplayWrapper {306 db,307 t: self,308 max_size,309 limited_size: None,310 omit_verbose_types: true,311 closure_style: ClosureStyle::ImplFn,312 display_target,313 display_kind: DisplayKind::Diagnostics,314 show_container_bounds: false,315 display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,316 }317 }318319 /// Returns a `Display`able type that is human-readable and tries to limit the number of items inside.320 /// Use this for showing definitions which may contain too many items, like `trait`, `struct`, `enum`321 fn display_limited<'a>(322 &'a self,323 db: &'db dyn HirDatabase,324 limited_size: Option<usize>,325 display_target: DisplayTarget,326 ) -> HirDisplayWrapper<'a, 'db, Self>327 where328 Self: Sized,329 {330 HirDisplayWrapper {331 db,332 t: self,333 max_size: None,334 limited_size,335 omit_verbose_types: true,336 closure_style: ClosureStyle::ImplFn,337 display_target,338 display_kind: DisplayKind::Diagnostics,339 show_container_bounds: false,340 display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,341 }342 }343344 /// Returns a String representation of `self` that can be inserted into the given module.345 /// Use this when generating code (e.g. assists)346 fn display_source_code<'a>(347 &'a self,348 db: &'db dyn HirDatabase,349 module_id: ModuleId,350 allow_opaque: bool,351 ) -> Result<String, DisplaySourceCodeError> {352 let mut result = String::new();353 let interner = DbInterner::new_with(db, module_id.krate(db));354 match self.hir_fmt(&mut HirFormatter {355 db,356 interner,357 fmt: &mut result,358 buf: String::with_capacity(20),359 curr_size: 0,360 max_size: None,361 entity_limit: None,362 omit_verbose_types: false,363 closure_style: ClosureStyle::ImplFn,364 display_target: DisplayTarget::from_crate(db, module_id.krate(db)),365 display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque },366 show_container_bounds: false,367 display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,368 currently_formatting_bounds: Default::default(),369 trait_bounds_need_parens: false,370 }) {371 Ok(()) => {}372 Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),373 Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e),374 };375 Ok(result)376 }377378 /// Returns a String representation of `self` for test purposes379 fn display_test<'a>(380 &'a self,381 db: &'db dyn HirDatabase,382 display_target: DisplayTarget,383 ) -> HirDisplayWrapper<'a, 'db, Self>384 where385 Self: Sized,386 {387 HirDisplayWrapper {388 db,389 t: self,390 max_size: None,391 limited_size: None,392 omit_verbose_types: false,393 closure_style: ClosureStyle::ImplFn,394 display_target,395 display_kind: DisplayKind::Test,396 show_container_bounds: false,397 display_lifetimes: DisplayLifetime::Always,398 }399 }400401 /// Returns a String representation of `self` that shows the constraint from402 /// the container for functions403 fn display_with_container_bounds<'a>(404 &'a self,405 db: &'db dyn HirDatabase,406 show_container_bounds: bool,407 display_target: DisplayTarget,408 ) -> HirDisplayWrapper<'a, 'db, Self>409 where410 Self: Sized,411 {412 HirDisplayWrapper {413 db,414 t: self,415 max_size: None,416 limited_size: None,417 omit_verbose_types: false,418 closure_style: ClosureStyle::ImplFn,419 display_target,420 display_kind: DisplayKind::Diagnostics,421 show_container_bounds,422 display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,423 }424 }425}426427impl<'db> HirFormatter<'_, 'db> {428 pub fn krate(&self) -> Crate {429 self.display_target.krate430 }431432 pub fn edition(&self) -> Edition {433 self.display_target.edition434 }435436 #[inline]437 pub fn lang_items(&self) -> &'db LangItems {438 self.interner.lang_items()439 }440441 pub fn write_joined<T: HirDisplay<'db>>(442 &mut self,443 iter: impl IntoIterator<Item = T>,444 sep: &str,445 ) -> Result {446 let mut first = true;447 for e in iter {448 if !first {449 write!(self, "{sep}")?;450 }451 first = false;452453 // Abbreviate multiple omitted types with a single ellipsis.454 if self.should_truncate() {455 return write!(self, "{TYPE_HINT_TRUNCATION}");456 }457458 e.hir_fmt(self)?;459 }460 Ok(())461 }462463 /// This allows using the `write!` macro directly with a `HirFormatter`.464 pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result {465 // We write to a buffer first to track output size466 self.buf.clear();467 fmt::write(&mut self.buf, args)?;468 self.curr_size += self.buf.len();469470 // Then we write to the internal formatter from the buffer471 self.fmt.write_str(&self.buf).map_err(HirDisplayError::from)472 }473474 pub fn write_str(&mut self, s: &str) -> Result {475 self.fmt.write_str(s)?;476 Ok(())477 }478479 pub fn write_char(&mut self, c: char) -> Result {480 self.fmt.write_char(c)?;481 Ok(())482 }483484 pub fn should_truncate(&self) -> bool {485 match self.max_size {486 Some(max_size) => self.curr_size >= max_size,487 None => false,488 }489 }490491 pub fn omit_verbose_types(&self) -> bool {492 self.omit_verbose_types493 }494495 pub fn show_container_bounds(&self) -> bool {496 self.show_container_bounds497 }498}499500#[derive(Debug, Clone, Copy)]501pub struct DisplayTarget {502 krate: Crate,503 pub edition: Edition,504}505506impl DisplayTarget {507 pub fn from_crate(db: &dyn HirDatabase, krate: Crate) -> Self {508 let edition = krate.data(db).edition;509 Self { krate, edition }510 }511}512513#[derive(Clone, Copy)]514pub enum DisplayKind {515 /// Display types for inlays, doc popups, autocompletion, etc...516 /// Showing `{unknown}` or not qualifying paths is fine here.517 /// There's no reason for this to fail.518 Diagnostics,519 /// Display types for inserting them in source files.520 /// The generated code should compile, so paths need to be qualified.521 SourceCode { target_module_id: ModuleId, allow_opaque: bool },522 /// Only for test purpose to keep real types523 Test,524}525526impl DisplayKind {527 fn is_source_code(self) -> bool {528 matches!(self, Self::SourceCode { .. })529 }530531 fn allows_opaque(self) -> bool {532 match self {533 Self::SourceCode { allow_opaque, .. } => allow_opaque,534 _ => true,535 }536 }537}538539#[derive(Debug)]540pub enum DisplaySourceCodeError {541 PathNotFound,542 Coroutine,543 OpaqueType,544 Cycle,545}546547pub enum HirDisplayError {548 /// Errors that can occur when generating source code549 DisplaySourceCodeError(DisplaySourceCodeError),550 /// `FmtError` is required to be compatible with std::fmt::Display551 FmtError,552}553impl From<fmt::Error> for HirDisplayError {554 fn from(_: fmt::Error) -> Self {555 Self::FmtError556 }557}558559pub struct HirDisplayWrapper<'a, 'db, T> {560 db: &'db dyn HirDatabase,561 t: &'a T,562 max_size: Option<usize>,563 limited_size: Option<usize>,564 omit_verbose_types: bool,565 closure_style: ClosureStyle,566 display_kind: DisplayKind,567 display_target: DisplayTarget,568 show_container_bounds: bool,569 display_lifetimes: DisplayLifetime,570}571572#[derive(Debug, PartialEq, Eq, Clone, Copy)]573pub enum ClosureStyle {574 /// `impl FnX(i32, i32) -> i32`, where `FnX` is the most special trait between `Fn`, `FnMut`, `FnOnce` that the575 /// closure implements. This is the default.576 ImplFn,577 /// `|i32, i32| -> i32`578 RANotation,579 /// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage.580 ClosureWithId,581 /// `{closure#14825}<i32, ()>`, useful for internal usage.582 ClosureWithSubst,583 /// `…`, which is the `TYPE_HINT_TRUNCATION`584 Hide,585}586587impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> {588 pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result {589 let krate = self.display_target.krate;590 let interner = DbInterner::new_with(self.db, krate);591 self.t.hir_fmt(&mut HirFormatter {592 db: self.db,593 interner,594 fmt: f,595 buf: String::with_capacity(self.max_size.unwrap_or(20)),596 curr_size: 0,597 max_size: self.max_size,598 entity_limit: self.limited_size,599 omit_verbose_types: self.omit_verbose_types,600 display_kind: self.display_kind,601 display_target: self.display_target,602 closure_style: self.closure_style,603 show_container_bounds: self.show_container_bounds,604 display_lifetimes: self.display_lifetimes,605 currently_formatting_bounds: Default::default(),606 trait_bounds_need_parens: false,607 })608 }609610 pub fn with_closure_style(mut self, c: ClosureStyle) -> Self {611 self.closure_style = c;612 self613 }614615 pub fn with_lifetime_display(mut self, l: DisplayLifetime) -> Self {616 self.display_lifetimes = l;617 self618 }619}620621impl<'db, T> fmt::Display for HirDisplayWrapper<'_, 'db, T>622where623 T: HirDisplay<'db>,624{625 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {626 match self.write_to(f) {627 Ok(()) => Ok(()),628 Err(HirDisplayError::FmtError) => Err(fmt::Error),629 Err(HirDisplayError::DisplaySourceCodeError(_)) => {630 // This should never happen631 panic!(632 "HirDisplay::hir_fmt failed with DisplaySourceCodeError when calling Display::fmt!"633 )634 }635 }636 }637}638639const TYPE_HINT_TRUNCATION: &str = "…";640641impl<'db, T: HirDisplay<'db>> HirDisplay<'db> for &T {642 fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {643 HirDisplay::hir_fmt(*self, f)644 }645}646647impl<'db, T: HirDisplay<'db> + Internable> HirDisplay<'db> for Interned<T> {648 fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {649 HirDisplay::hir_fmt(&**self, f)650 }651}652653fn write_projection<'db>(654 f: &mut HirFormatter<'_, 'db>,655 alias: &AliasTy<'db>,656 needs_parens_if_multi: bool,657 def_id: TypeAliasId,658) -> Result {659 f.format_bounds_with(*alias, |f| {660 if f.should_truncate() {661 return write!(f, "{TYPE_HINT_TRUNCATION}");662 }663 let trait_ref = alias.trait_ref(f.interner);664 let self_ty = trait_ref.self_ty();665666 // if we are projection on a type parameter, check if the projection target has bounds667 // itself, if so, we render them directly as `impl Bound` instead of the less useful668 // `<Param as Trait>::Assoc`669 if !f.display_kind.is_source_code()670 && let TyKind::Param(param) = self_ty.kind()671 {672 // FIXME: We shouldn't use `param.id`, it should be removed. We should know the673 // `GenericDefId` from the formatted type (store it inside the `HirFormatter`).674 let bounds = GenericPredicates::query_all(f.db, param.id.parent())675 .iter_identity()676 .map(Unnormalized::skip_norm_wip)677 .filter(|wc| {678 let ty = match wc.kind().skip_binder() {679 ClauseKind::Trait(tr) => tr.self_ty(),680 ClauseKind::TypeOutlives(t) => t.0,681 _ => return false,682 };683 let TyKind::Alias(a) = ty.kind() else {684 return false;685 };686 a == *alias687 })688 .collect::<Vec<_>>();689 if !bounds.is_empty() {690 return write_bounds_like_dyn_trait_with_prefix(691 f,692 "impl",693 Either::Left(Ty::new_alias(f.interner, *alias)),694 &bounds,695 SizedByDefault::NotSized,696 needs_parens_if_multi,697 );698 }699 }700701 write!(f, "<")?;702 self_ty.hir_fmt(f)?;703 write!(f, " as ")?;704 trait_ref.hir_fmt(f)?;705 write!(f, ">::{}", TypeAliasSignature::of(f.db, def_id).name.display(f.db, f.edition()))?;706 let proj_params = &alias.args.as_slice()[trait_ref.args.len()..];707 hir_fmt_generics(f, proj_params, None, None)708 })709}710711impl<'db> HirDisplay<'db> for GenericArg<'db> {712 fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {713 match self.kind() {714 GenericArgKind::Type(ty) => ty.hir_fmt(f),715 GenericArgKind::Lifetime(lt) => lt.hir_fmt(f),716 GenericArgKind::Const(c) => c.hir_fmt(f),717 }718 }719}720721impl<'db> HirDisplay<'db> for Allocation<'db> {722 fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {723 render_const_scalar(f, &self.memory, &self.memory_map, self.ty)724 }725}726727impl<'db> HirDisplay<'db> for Const<'db> {728 fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {729 match self.kind() {730 ConstKind::Placeholder(_) => write!(f, "<placeholder>"),731 ConstKind::Bound(BoundVarIndexKind::Bound(db), bound_const) => {732 write!(f, "?{}.{}", db.as_u32(), bound_const.var.as_u32())733 }734 ConstKind::Bound(BoundVarIndexKind::Canonical, bound_const) => {735 write!(f, "?c.{}", bound_const.var.as_u32())736 }737 ConstKind::Infer(..) => write!(f, "#c#"),738 ConstKind::Param(param) => {739 let generics = GenericParams::of(f.db, param.id.parent());740 let param_data = &generics[param.id.local_id()];741 f.start_location_link_generic(param.id.into());742 write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?;743 f.end_location_link();744 Ok(())745 }746 ConstKind::Value(value) => render_const_scalar_from_valtree(f, value.ty, value.value),747 ConstKind::Unevaluated(unev) => {748 let c = unev.def.0;749 match c {750 GeneralConstId::ConstId(id) => match &ConstSignature::of(f.db, id).name {751 Some(name) => {752 f.start_location_link(id.into());753 write!(f, "{}", name.display(f.db, f.edition()))?;754 f.end_location_link();755 }756 None => f.write_str("_")?,757 },758 GeneralConstId::StaticId(id) => {759 let name = &StaticSignature::of(f.db, id).name;760 f.start_location_link(id.into());761 write!(f, "{}", name.display(f.db, f.edition()))?;762 f.end_location_link();763 }764 GeneralConstId::AnonConstId(_) => {765 f.write_str(if f.display_kind.is_source_code() { "_" } else { "{const}" })?766 }767 };768 hir_fmt_generics(f, unev.args.as_slice(), c.generic_def(f.db), None)?;769 Ok(())770 }771 ConstKind::Error(..) => f.write_char('_'),772 ConstKind::Expr(..) => write!(f, "<const-expr>"),773 }774 }775}776777fn render_const_scalar<'db>(778 f: &mut HirFormatter<'_, 'db>,779 b: &[u8],780 memory_map: &MemoryMap<'db>,781 ty: Ty<'db>,782) -> Result {783 let param_env = ParamEnv::empty(f.interner);784 let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis);785 let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty);786 render_const_scalar_inner(f, b, memory_map, ty, param_env)787}788789fn render_const_scalar_inner<'db>(790 f: &mut HirFormatter<'_, 'db>,791 b: &[u8],792 memory_map: &MemoryMap<'db>,793 ty: Ty<'db>,794 param_env: ParamEnv<'db>,795) -> Result {796 use TyKind;797 let param_env = ParamEnvAndCrate { param_env, krate: f.krate() };798 match ty.kind() {799 TyKind::Bool => write!(f, "{}", b[0] != 0),800 TyKind::Char => {801 let it = u128::from_le_bytes(pad16(b, false)) as u32;802 let Ok(c) = char::try_from(it) else {803 return f.write_str("<unicode-error>");804 };805 write!(f, "{c:?}")806 }807 TyKind::Int(_) => {808 let it = i128::from_le_bytes(pad16(b, true));809 write!(f, "{it}")810 }811 TyKind::Uint(_) => {812 let it = u128::from_le_bytes(pad16(b, false));813 write!(f, "{it}")814 }815 TyKind::Float(fl) => match fl {816 FloatTy::F16 => {817 // FIXME(#17451): Replace with builtins once they are stabilised.818 let it = f16::from_bits(u16::from_le_bytes(b.try_into().unwrap()).into());819 let s = it.to_string();820 if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {821 // Match Rust debug formatting822 write!(f, "{s}.0")823 } else {824 write!(f, "{s}")825 }826 }827 FloatTy::F32 => {828 let it = f32::from_le_bytes(b.try_into().unwrap());829 write!(f, "{it:?}")830 }831 FloatTy::F64 => {832 let it = f64::from_le_bytes(b.try_into().unwrap());833 write!(f, "{it:?}")834 }835 FloatTy::F128 => {836 // FIXME(#17451): Replace with builtins once they are stabilised.837 let it = f128::from_bits(u128::from_le_bytes(b.try_into().unwrap()));838 let s = it.to_string();839 if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {840 // Match Rust debug formatting841 write!(f, "{s}.0")842 } else {843 write!(f, "{s}")844 }845 }846 },847 TyKind::Ref(_, t, _) => match t.kind() {848 TyKind::Str => {849 let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());850 let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());851 let Some(bytes) = memory_map.get(addr, size) else {852 return f.write_str("<ref-data-not-available>");853 };854 let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");855 write!(f, "{s:?}")856 }857 TyKind::Slice(ty) => {858 let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());859 let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());860 let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else {861 return f.write_str("<layout-error>");862 };863 let size_one = layout.size.bytes_usize();864 let Some(bytes) = memory_map.get(addr, size_one * count) else {865 return f.write_str("<ref-data-not-available>");866 };867 let expected_len = count * size_one;868 if bytes.len() < expected_len {869 never!(870 "Memory map size is too small. Expected {expected_len}, got {}",871 bytes.len(),872 );873 return f.write_str("<layout-error>");874 }875 f.write_str("&[")?;876 let mut first = true;877 for i in 0..count {878 if first {879 first = false;880 } else {881 f.write_str(", ")?;882 }883 let offset = size_one * i;884 render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, ty)?;885 }886 f.write_str("]")887 }888 TyKind::Dynamic(_, _) => {889 let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());890 let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());891 let Ok(t) = memory_map.vtable_ty(ty_id) else {892 return f.write_str("<ty-missing-in-vtable-map>");893 };894 let Ok(layout) = f.db.layout_of_ty(t.store(), param_env.store()) else {895 return f.write_str("<layout-error>");896 };897 let size = layout.size.bytes_usize();898 let Some(bytes) = memory_map.get(addr, size) else {899 return f.write_str("<ref-data-not-available>");900 };901 f.write_str("&")?;902 render_const_scalar(f, bytes, memory_map, t)903 }904 TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.def_id() {905 hir_def::AdtId::StructId(s) => {906 let data = StructSignature::of(f.db, s);907 write!(f, "&{}", data.name.display(f.db, f.edition()))?;908 Ok(())909 }910 _ => f.write_str("<unsized-enum-or-union>"),911 },912 _ => {913 let addr = usize::from_le_bytes(match b.try_into() {914 Ok(b) => b,915 Err(_) => {916 never!(917 "tried rendering ty {:?} in const ref with incorrect byte count {}",918 t,919 b.len()920 );921 return f.write_str("<layout-error>");922 }923 });924 let Ok(layout) = f.db.layout_of_ty(t.store(), param_env.store()) else {925 return f.write_str("<layout-error>");926 };927 let size = layout.size.bytes_usize();928 let Some(bytes) = memory_map.get(addr, size) else {929 return f.write_str("<ref-data-not-available>");930 };931 f.write_str("&")?;932 render_const_scalar(f, bytes, memory_map, t)933 }934 },935 TyKind::Tuple(tys) => {936 let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else {937 return f.write_str("<layout-error>");938 };939 f.write_str("(")?;940 let mut first = true;941 for (id, ty) in tys.iter().enumerate() {942 if first {943 first = false;944 } else {945 f.write_str(", ")?;946 }947 let offset = layout.fields.offset(id).bytes_usize();948 let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else {949 f.write_str("<layout-error>")?;950 continue;951 };952 let size = layout.size.bytes_usize();953 render_const_scalar(f, &b[offset..offset + size], memory_map, ty)?;954 }955 f.write_str(")")956 }957 TyKind::Adt(def, args) => {958 let def = def.def_id();959 let Ok(layout) = f.db.layout_of_adt(def, args.store(), param_env.store()) else {960 return f.write_str("<layout-error>");961 };962 match def {963 hir_def::AdtId::StructId(s) => {964 let data = StructSignature::of(f.db, s);965 write!(f, "{}", data.name.display(f.db, f.edition()))?;966 let field_types = f.db.field_types(s.into());967 render_variant_after_name(968 s.fields(f.db),969 f,970 field_types,971 f.db.trait_environment(def.into()),972 &layout,973 args,974 b,975 memory_map,976 )977 }978 hir_def::AdtId::UnionId(u) => {979 write!(f, "{}", UnionSignature::of(f.db, u).name.display(f.db, f.edition()))980 }981 hir_def::AdtId::EnumId(e) => {982 let Ok(target_data_layout) = f.db.target_data_layout(f.krate()) else {983 return f.write_str("<target-layout-not-available>");984 };985 let Some((var_id, var_layout)) =986 detect_variant_from_bytes(&layout, f.db, target_data_layout, b, e)987 else {988 return f.write_str("<failed-to-detect-variant>");989 };990 let loc = var_id.lookup(f.db);991 write!(f, "{}", loc.name.display(f.db, f.edition()))?;992 let field_types = f.db.field_types(var_id.into());993 render_variant_after_name(994 var_id.fields(f.db),995 f,996 field_types,997 f.db.trait_environment(def.into()),998 var_layout,999 args,1000 b,1001 memory_map,1002 )1003 }1004 }1005 }1006 TyKind::FnDef(..) => ty.hir_fmt(f),1007 TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => {1008 let it = u128::from_le_bytes(pad16(b, false));1009 write!(f, "{it:#X} as ")?;1010 ty.hir_fmt(f)1011 }1012 TyKind::Array(ty, len) => {1013 let Some(len) = consteval::try_const_usize(f.db, len) else {1014 return f.write_str("<unknown-array-len>");1015 };1016 let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else {1017 return f.write_str("<layout-error>");1018 };1019 let size_one = layout.size.bytes_usize();1020 f.write_str("[")?;1021 let mut first = true;1022 for i in 0..len as usize {1023 if first {1024 first = false;1025 } else {1026 f.write_str(", ")?;1027 }1028 let offset = size_one * i;1029 render_const_scalar(f, &b[offset..offset + size_one], memory_map, ty)?;1030 }1031 f.write_str("]")1032 }1033 TyKind::Never => f.write_str("!"),1034 TyKind::Closure(_, _) => f.write_str("<closure>"),1035 TyKind::Coroutine(_, _) => f.write_str("<coroutine>"),1036 TyKind::CoroutineWitness(_, _) => f.write_str("<coroutine-witness>"),1037 TyKind::CoroutineClosure(_, _) => f.write_str("<coroutine-closure>"),1038 TyKind::UnsafeBinder(_) => f.write_str("<unsafe-binder>"),1039 // The below arms are unreachable, since const eval will bail out before here.1040 TyKind::Foreign(_) => f.write_str("<extern-type>"),1041 TyKind::Pat(_, _) => f.write_str("<pat>"),1042 TyKind::Error(..)1043 | TyKind::Placeholder(_)1044 | TyKind::Alias(..)1045 | TyKind::Param(_)1046 | TyKind::Bound(_, _)1047 | TyKind::Infer(_) => f.write_str("<placeholder-or-unknown-type>"),1048 // The below arms are unreachable, since we handled them in ref case.1049 TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _) => f.write_str("<unsized-value>"),1050 }1051}10521053fn render_const_scalar_from_valtree<'db>(1054 f: &mut HirFormatter<'_, 'db>,1055 ty: Ty<'db>,1056 valtree: ValTree<'db>,1057) -> Result {1058 let param_env = ParamEnv::empty(f.interner);1059 let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis);1060 let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty);1061 render_const_scalar_from_valtree_inner(f, ty, valtree, param_env)1062}10631064fn render_const_scalar_from_valtree_inner<'db>(1065 f: &mut HirFormatter<'_, 'db>,1066 ty: Ty<'db>,1067 valtree: ValTree<'db>,1068 _param_env: ParamEnv<'db>,1069) -> Result {1070 use TyKind;1071 match ty.kind() {1072 TyKind::Bool => write!(f, "{}", valtree.inner().to_leaf().try_to_bool().unwrap()),1073 TyKind::Char => {1074 let it = valtree.inner().to_leaf().to_u32();1075 let Ok(c) = char::try_from(it) else {1076 return f.write_str("<unicode-error>");1077 };1078 write!(f, "{c:?}")1079 }1080 TyKind::Int(_) => {1081 let it = valtree.inner().to_leaf().to_int_unchecked();1082 write!(f, "{it}")1083 }1084 TyKind::Uint(_) => {1085 let it = valtree.inner().to_leaf().to_uint_unchecked();1086 write!(f, "{it}")1087 }1088 TyKind::Float(fl) => match fl {1089 FloatTy::F16 => {1090 // FIXME(#17451): Replace with builtins once they are stabilised.1091 let it = f16::from_bits(valtree.inner().to_leaf().to_u16() as u128);1092 let s = it.to_string();1093 if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {1094 // Match Rust debug formatting1095 write!(f, "{s}.0")1096 } else {1097 write!(f, "{s}")1098 }1099 }1100 FloatTy::F32 => {1101 let it = f32::from_bits(valtree.inner().to_leaf().to_u32());1102 write!(f, "{it:?}")1103 }1104 FloatTy::F64 => {1105 let it = f64::from_bits(valtree.inner().to_leaf().to_u64());1106 write!(f, "{it:?}")1107 }1108 FloatTy::F128 => {1109 // FIXME(#17451): Replace with builtins once they are stabilised.1110 let it = f128::from_bits(valtree.inner().to_leaf().to_u128());1111 let s = it.to_string();1112 if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {1113 // Match Rust debug formatting1114 write!(f, "{s}.0")1115 } else {1116 write!(f, "{s}")1117 }1118 }1119 },1120 TyKind::Ref(_, inner_ty, _) => {1121 render_const_scalar_from_valtree_inner(f, inner_ty, valtree, _param_env)1122 }1123 TyKind::Str => {1124 let bytes = valtree1125 .inner()1126 .to_branch()1127 .iter()1128 .map(|konst| match konst.kind() {1129 ConstKind::Value(value) => Some(value.value.inner().to_leaf().to_u8()),1130 _ => None,1131 })1132 .collect::<Option<Vec<_>>>();1133 let Some(bytes) = bytes else { return f.write_str("<invalid-str>") };1134 let s = std::str::from_utf8(&bytes).unwrap_or("<utf8-error>");1135 write!(f, "{s:?}")1136 }1137 TyKind::Slice(inner_ty) | TyKind::Array(inner_ty, _) => {1138 let mut first = true;1139 write!(f, "[")?;1140 for item in valtree.inner().to_branch() {1141 if !first {1142 write!(f, ", ")?;1143 } else {1144 first = false;1145 }1146 let ConstKind::Value(value) = item.kind() else {1147 return f.write_str("<invalid-const>");1148 };1149 render_const_scalar_from_valtree_inner(f, inner_ty, value.value, _param_env)?;1150 }1151 write!(f, "]")1152 }1153 TyKind::Tuple(tys) => {1154 let mut first = true;1155 write!(f, "(")?;1156 for (inner_ty, item) in std::iter::zip(tys, valtree.inner().to_branch()) {1157 if !first {1158 write!(f, ", ")?;1159 } else {1160 first = false;1161 }1162 let ConstKind::Value(value) = item.kind() else {1163 return f.write_str("<invalid-const>");1164 };1165 render_const_scalar_from_valtree_inner(f, inner_ty, value.value, _param_env)?;1166 }1167 write!(f, ")")1168 }1169 TyKind::Adt(..) => {1170 // FIXME: ADTs, requires `adt_const_params`.1171 f.write_str("<adt>")1172 }1173 TyKind::FnDef(..) => ty.hir_fmt(f),1174 TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => {1175 let it = valtree.inner().to_leaf().to_uint_unchecked();1176 write!(f, "{it:#X} as ")?;1177 ty.hir_fmt(f)1178 }1179 TyKind::Never => f.write_str("!"),1180 TyKind::Closure(_, _) => f.write_str("<closure>"),1181 TyKind::Coroutine(_, _) => f.write_str("<coroutine>"),1182 TyKind::CoroutineWitness(_, _) => f.write_str("<coroutine-witness>"),1183 TyKind::CoroutineClosure(_, _) => f.write_str("<coroutine-closure>"),1184 TyKind::UnsafeBinder(_) => f.write_str("<unsafe-binder>"),1185 // The below arms are unreachable, since const eval will bail out before here.1186 TyKind::Foreign(_) => f.write_str("<extern-type>"),1187 TyKind::Pat(_, _) => f.write_str("<pat>"),1188 TyKind::Error(..)1189 | TyKind::Placeholder(_)1190 | TyKind::Alias(..)1191 | TyKind::Param(_)1192 | TyKind::Bound(_, _)1193 | TyKind::Infer(_) => f.write_str("<placeholder-or-unknown-type>"),1194 TyKind::Dynamic(_, _) => f.write_str("<dyn-trait>"),1195 }1196}11971198fn render_variant_after_name<'db>(1199 data: &VariantFields,1200 f: &mut HirFormatter<'_, 'db>,1201 field_types: &'db ArenaMap<LocalFieldId, FieldType>,1202 param_env: ParamEnv<'db>,1203 layout: &Layout,1204 args: GenericArgs<'db>,1205 b: &[u8],1206 memory_map: &MemoryMap<'db>,1207) -> Result {1208 let param_env = ParamEnvAndCrate { param_env, krate: f.krate() };1209 match data.shape {1210 FieldsShape::Record | FieldsShape::Tuple => {1211 let render_field = |f: &mut HirFormatter<'_, 'db>, id: LocalFieldId| {1212 let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize();1213 let ty = field_types[id].ty().instantiate(f.interner, args).skip_norm_wip();1214 let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else {1215 return f.write_str("<layout-error>");1216 };1217 let size = layout.size.bytes_usize();1218 render_const_scalar(f, &b[offset..offset + size], memory_map, ty)1219 };1220 let mut it = data.fields().iter();1221 if matches!(data.shape, FieldsShape::Record) {1222 write!(f, " {{")?;1223 if let Some((id, data)) = it.next() {1224 write!(f, " {}: ", data.name.display(f.db, f.edition()))?;1225 render_field(f, id)?;1226 }1227 for (id, data) in it {1228 write!(f, ", {}: ", data.name.display(f.db, f.edition()))?;1229 render_field(f, id)?;1230 }1231 write!(f, " }}")?;1232 } else {1233 let mut it = it.map(|it| it.0);1234 write!(f, "(")?;1235 if let Some(id) = it.next() {1236 render_field(f, id)?;1237 }1238 for id in it {1239 write!(f, ", ")?;1240 render_field(f, id)?;1241 }1242 write!(f, ")")?;1243 }1244 Ok(())1245 }1246 FieldsShape::Unit => Ok(()),1247 }1248}12491250impl<'db> HirDisplay<'db> for Ty<'db> {1251 fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>) -> Result {1252 let interner = f.interner;1253 if f.should_truncate() {1254 return write!(f, "{TYPE_HINT_TRUNCATION}");1255 }12561257 let trait_bounds_need_parens = mem::replace(&mut f.trait_bounds_need_parens, false);1258 match self.kind() {1259 TyKind::Never => write!(f, "!")?,1260 TyKind::Str => write!(f, "str")?,1261 TyKind::Bool => write!(f, "bool")?,1262 TyKind::Char => write!(f, "char")?,1263 TyKind::Float(t) => write!(f, "{}", primitive::float_ty_to_string(t))?,1264 TyKind::Int(t) => write!(f, "{}", primitive::int_ty_to_string(t))?,1265 TyKind::Uint(t) => write!(f, "{}", primitive::uint_ty_to_string(t))?,1266 TyKind::Slice(t) => {1267 write!(f, "[")?;1268 t.hir_fmt(f)?;1269 write!(f, "]")?;1270 }1271 TyKind::Array(t, c) => {1272 write!(f, "[")?;1273 t.hir_fmt(f)?;1274 write!(f, "; ")?;1275 c.hir_fmt(f)?;1276 write!(f, "]")?;1277 }1278 TyKind::Ref(l, t, m) => {1279 f.write_char('&')?;1280 if f.render_region(l) {1281 l.hir_fmt(f)?;1282 f.write_char(' ')?;1283 }1284 match m {1285 rustc_ast_ir::Mutability::Not => (),1286 rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?,1287 }12881289 f.trait_bounds_need_parens = true;1290 t.hir_fmt(f)?;1291 f.trait_bounds_need_parens = false;1292 }1293 TyKind::RawPtr(t, m) => {1294 write!(1295 f,1296 "*{}",1297 match m {1298 rustc_ast_ir::Mutability::Not => "const ",1299 rustc_ast_ir::Mutability::Mut => "mut ",1300 }1301 )?;13021303 f.trait_bounds_need_parens = true;1304 t.hir_fmt(f)?;1305 f.trait_bounds_need_parens = false;1306 }1307 TyKind::Tuple(tys) => {1308 if tys.len() == 1 {1309 write!(f, "(")?;1310 tys.as_slice()[0].hir_fmt(f)?;1311 write!(f, ",)")?;1312 } else {1313 write!(f, "(")?;1314 f.write_joined(tys.as_slice(), ", ")?;1315 write!(f, ")")?;1316 }1317 }1318 TyKind::FnPtr(sig, header) => {1319 let sig = sig.with(header);1320 sig.hir_fmt(f)?;1321 }1322 TyKind::FnDef(def, args) => {1323 let def = def.0;1324 let sig =1325 db.callable_item_signature(def).instantiate(interner, args).skip_norm_wip();13261327 if f.display_kind.is_source_code() {1328 // `FnDef` is anonymous and there's no surface syntax for it. Show it as a1329 // function pointer type.1330 return sig.hir_fmt(f);1331 }1332 if let Safety::Unsafe = sig.safety() {1333 write!(f, "unsafe ")?;1334 }1335 if !sig.abi().is_rustic_abi() {1336 f.write_str("extern \"")?;1337 f.write_str(sig.abi().as_str())?;1338 f.write_str("\" ")?;1339 }13401341 let sig = sig.skip_binder();1342 write!(f, "fn ")?;1343 f.start_location_link(def.into());1344 match def {1345 CallableDefId::FunctionId(ff) => write!(1346 f,1347 "{}",1348 FunctionSignature::of(db, ff).name.display(f.db, f.edition())1349 )?,1350 CallableDefId::StructId(s) => {1351 write!(f, "{}", StructSignature::of(db, s).name.display(f.db, f.edition()))?1352 }1353 CallableDefId::EnumVariantId(e) => {1354 let loc = e.lookup(db);1355 write!(f, "{}", loc.name.display(db, f.edition()))?1356 }1357 };1358 f.end_location_link();13591360 if !args.is_empty() {1361 let generic_def_id = GenericDefId::from_callable(db, def);1362 let generics = generics(db, generic_def_id);1363 let ProvenanceSplit {1364 parent_total: parent_len,1365 has_self_param: self_param,1366 non_impl_trait_type_params: type_,1367 const_params: const_,1368 impl_trait_type_params: impl_,1369 lifetimes: lifetime,1370 } = generics.provenance_split();1371 let parameters = args.as_slice();1372 debug_assert_eq!(1373 parameters.len(),1374 parent_len + self_param as usize + type_ + const_ + impl_ + lifetime1375 );1376 // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?1377 if parameters.len() - impl_ > 0 {1378 let params_len = parameters.len();1379 // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes1380 let parameters =1381 generic_args_sans_defaults(f, Some(generic_def_id), parameters);1382 assert!(params_len >= parameters.len());1383 let defaults = params_len - parameters.len();13841385 // Normally, functions cannot have default parameters, but they can,1386 // for function-like things such as struct names or enum variants.1387 // The former cannot have defaults but does have parents,1388 // but the latter cannot have parents but can have defaults.1389 //1390 // However, it's also true that *traits* can have defaults too.1391 // In this case, there can be no function params.1392 let parent_end = if parent_len > 0 {1393 // If `parent_len` > 0, then there cannot be defaults on the function1394 // and all defaults must come from the parent.1395 parent_len - defaults1396 } else {1397 parent_len1398 };1399 let fn_params_no_impl_or_defaults = parameters.len() - parent_end - impl_;1400 let (parent_params, fn_params) = parameters.split_at(parent_end);14011402 write!(f, "<")?;1403 hir_fmt_generic_arguments(f, parent_params, None)?;1404 if !parent_params.is_empty() && !fn_params.is_empty() {1405 write!(f, ", ")?;1406 }1407 hir_fmt_generic_arguments(1408 f,1409 &fn_params[..fn_params_no_impl_or_defaults],1410 None,1411 )?;1412 write!(f, ">")?;1413 }1414 }1415 write!(f, "(")?;1416 f.write_joined(sig.inputs(), ", ")?;1417 write!(f, ")")?;1418 let ret = sig.output();1419 if !ret.is_unit() {1420 write!(f, " -> ")?;1421 ret.hir_fmt(f)?;1422 }1423 }1424 TyKind::Adt(def, parameters) => {1425 let def_id = def.def_id();1426 f.start_location_link(def_id.into());1427 match f.display_kind {1428 DisplayKind::Diagnostics | DisplayKind::Test => {1429 let name = match def_id {1430 hir_def::AdtId::StructId(it) => {1431 StructSignature::of(db, it).name.clone()1432 }1433 hir_def::AdtId::UnionId(it) => UnionSignature::of(db, it).name.clone(),1434 hir_def::AdtId::EnumId(it) => EnumSignature::of(db, it).name.clone(),1435 };1436 write!(f, "{}", name.display(f.db, f.edition()))?;1437 }1438 DisplayKind::SourceCode { target_module_id: module_id, allow_opaque: _ } => {1439 if let Some(path) = find_path::find_path(1440 db,1441 ItemInNs::Types(def_id.into()),1442 module_id,1443 PrefixKind::Plain,1444 false,1445 // FIXME: no_std Cfg?1446 FindPathConfig {1447 prefer_no_std: false,1448 prefer_prelude: true,1449 prefer_absolute: false,1450 allow_unstable: true,1451 },1452 ) {1453 write!(f, "{}", path.display(f.db, f.edition()))?;1454 } else {1455 return Err(HirDisplayError::DisplaySourceCodeError(1456 DisplaySourceCodeError::PathNotFound,1457 ));1458 }1459 }1460 }1461 f.end_location_link();14621463 hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().into()), None)?;1464 }1465 TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) => {1466 write_projection(f, &alias_ty, trait_bounds_need_parens, def_id.0)?1467 }1468 TyKind::Foreign(alias) => {1469 let type_alias = TypeAliasSignature::of(db, alias.0);1470 f.start_location_link(alias.0.into());1471 write!(f, "{}", type_alias.name.display(f.db, f.edition()))?;1472 f.end_location_link();1473 }1474 TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Opaque { def_id }, .. }) => {1475 let opaque_ty_id = def_id.0;1476 if !f.display_kind.allows_opaque() {1477 return Err(HirDisplayError::DisplaySourceCodeError(1478 DisplaySourceCodeError::OpaqueType,1479 ));1480 }1481 let impl_trait_id = opaque_ty_id.loc(db);1482 let data = impl_trait_id.predicates(db);1483 let bounds = data1484 .iter_instantiated_copied(interner, alias_ty.args.as_slice())1485 .map(Unnormalized::skip_norm_wip)1486 .collect::<Vec<_>>();1487 let krate = match impl_trait_id {1488 ImplTraitId::ReturnTypeImplTrait(func, _) => {1489 func.krate(db)1490 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution1491 }1492 ImplTraitId::TypeAliasImplTrait(alias, _) => alias.krate(db),1493 };1494 write_bounds_like_dyn_trait_with_prefix(1495 f,1496 "impl",1497 Either::Left(*self),1498 &bounds,1499 SizedByDefault::Sized { anchor: krate },1500 trait_bounds_need_parens,1501 )?;1502 }1503 TyKind::Closure(id, substs) => {1504 let id = id.0;1505 if f.display_kind.is_source_code() {1506 if !f.display_kind.allows_opaque() {1507 return Err(HirDisplayError::DisplaySourceCodeError(1508 DisplaySourceCodeError::OpaqueType,1509 ));1510 } else if f.closure_style != ClosureStyle::ImplFn {1511 never!("Only `impl Fn` is valid for displaying closures in source code");1512 }1513 }1514 match f.closure_style {1515 ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),1516 ClosureStyle::ClosureWithId => {1517 return write!(1518 f,1519 "{{closure#{:?}}}",1520 salsa::plumbing::AsId::as_id(&id).index()1521 );1522 }1523 ClosureStyle::ClosureWithSubst => {1524 write!(f, "{{closure#{:?}}}", salsa::plumbing::AsId::as_id(&id).index())?;1525 return hir_fmt_generics(f, substs.as_slice(), None, None);1526 }1527 _ => (),1528 }1529 let sig = interner.signature_unclosure(substs.as_closure().sig(), Safety::Safe);1530 let sig = sig.skip_binder();1531 let kind = substs.as_closure().kind();1532 match f.closure_style {1533 ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,1534 ClosureStyle::RANotation => write!(f, "|")?,1535 _ => unreachable!(),1536 }1537 if sig.inputs().is_empty() {1538 } else if f.should_truncate() {1539 write!(f, "{TYPE_HINT_TRUNCATION}")?;1540 } else {1541 f.write_joined(sig.inputs(), ", ")?;1542 };1543 match f.closure_style {1544 ClosureStyle::ImplFn => write!(f, ")")?,1545 ClosureStyle::RANotation => write!(f, "|")?,1546 _ => unreachable!(),1547 }1548 if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() {1549 write!(f, " -> ")?;1550 sig.output().hir_fmt(f)?;1551 }1552 }1553 TyKind::CoroutineClosure(id, args) => {1554 let id = id.0;1555 let closure_kind = match id.loc(db).kind {1556 HirClosureKind::CoroutineClosure(kind) => kind,1557 kind => panic!("invalid kind for coroutine closure: {kind:?}"),1558 };1559 let closure_label = match closure_kind {1560 CoroutineKind::Async => "async closure",1561 CoroutineKind::Gen => "gen closure",1562 CoroutineKind::AsyncGen => "async gen closure",1563 };1564 if f.display_kind.is_source_code() {1565 if !f.display_kind.allows_opaque() {1566 return Err(HirDisplayError::DisplaySourceCodeError(1567 DisplaySourceCodeError::OpaqueType,1568 ));1569 } else if f.closure_style != ClosureStyle::ImplFn {1570 never!("Only `impl Fn` is valid for displaying closures in source code");1571 }1572 }1573 match f.closure_style {1574 ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),1575 ClosureStyle::ClosureWithId => {1576 return write!(1577 f,1578 "{{{closure_label}#{:?}}}",1579 salsa::plumbing::AsId::as_id(&id).index()1580 );1581 }1582 ClosureStyle::ClosureWithSubst => {1583 write!(1584 f,1585 "{{{closure_label}#{:?}}}",1586 salsa::plumbing::AsId::as_id(&id).index()1587 )?;1588 return hir_fmt_generics(f, args.as_slice(), None, None);1589 }1590 _ => (),1591 }1592 let callable_kind = args.as_coroutine_closure().kind();1593 let kind = match (closure_kind, callable_kind) {1594 (CoroutineKind::Async, rustc_type_ir::ClosureKind::Fn) => "AsyncFn",1595 (CoroutineKind::Async, rustc_type_ir::ClosureKind::FnMut) => "AsyncFnMut",1596 (CoroutineKind::Async, rustc_type_ir::ClosureKind::FnOnce) => "AsyncFnOnce",1597 (_, rustc_type_ir::ClosureKind::Fn) => "Fn",1598 (_, rustc_type_ir::ClosureKind::FnMut) => "FnMut",1599 (_, rustc_type_ir::ClosureKind::FnOnce) => "FnOnce",1600 };1601 let coroutine_sig = args.as_coroutine_closure().coroutine_closure_sig();1602 let coroutine_sig = coroutine_sig.skip_binder();1603 let coroutine_inputs = coroutine_sig.tupled_inputs_ty.tuple_fields();1604 let coroutine_output = coroutine_sig.return_ty;1605 match f.closure_style {1606 ClosureStyle::ImplFn => write!(f, "impl {kind}(")?,1607 ClosureStyle::RANotation => match closure_kind {1608 CoroutineKind::Async => write!(f, "async |")?,1609 CoroutineKind::Gen => write!(f, "gen |")?,1610 CoroutineKind::AsyncGen => write!(f, "async gen |")?,1611 },1612 _ => unreachable!(),1613 }1614 if coroutine_inputs.is_empty() {1615 } else if f.should_truncate() {1616 write!(f, "{TYPE_HINT_TRUNCATION}")?;1617 } else {1618 f.write_joined(coroutine_inputs, ", ")?;1619 };1620 match f.closure_style {1621 ClosureStyle::ImplFn => write!(f, ")")?,1622 ClosureStyle::RANotation => write!(f, "|")?,1623 _ => unreachable!(),1624 }1625 if f.closure_style == ClosureStyle::RANotation || !coroutine_output.is_unit() {1626 write!(f, " -> ")?;1627 coroutine_output.hir_fmt(f)?;1628 }1629 }1630 TyKind::Placeholder(_) => write!(f, "{{placeholder}}")?,1631 TyKind::Param(param) => {1632 // FIXME: We should not access `param.id`, it should be removed, and we should know the1633 // parent from the formatted type.1634 let generics = GenericParams::of(db, param.id.parent());1635 let param_data = &generics[param.id.local_id()];1636 match param_data {1637 TypeOrConstParamData::TypeParamData(p) => match p.provenance {1638 TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {1639 f.start_location_link_generic(param.id.into());1640 write!(1641 f,1642 "{}",1643 p.name1644 .clone()1645 .unwrap_or_else(Name::missing)1646 .display(f.db, f.edition())1647 )?;1648 f.end_location_link();1649 }1650 TypeParamProvenance::ArgumentImplTrait => {1651 let bounds = GenericPredicates::query_all(f.db, param.id.parent())1652 .iter_identity()1653 .map(Unnormalized::skip_norm_wip)1654 .filter(|wc| match wc.kind().skip_binder() {1655 ClauseKind::Trait(tr) => tr.self_ty() == *self,1656 ClauseKind::Projection(proj) => proj.self_ty() == *self,1657 ClauseKind::TypeOutlives(to) => to.0 == *self,1658 _ => false,1659 })1660 .collect::<Vec<_>>();1661 let krate = param.id.parent().module(db).krate(db);1662 write_bounds_like_dyn_trait_with_prefix(1663 f,1664 "impl",1665 Either::Left(*self),1666 &bounds,1667 SizedByDefault::Sized { anchor: krate },1668 trait_bounds_need_parens,1669 )?;1670 }1671 },1672 TypeOrConstParamData::ConstParamData(p) => {1673 f.start_location_link_generic(param.id.into());1674 write!(f, "{}", p.name.display(f.db, f.edition()))?;1675 f.end_location_link();1676 }1677 }1678 }1679 TyKind::Bound(BoundVarIndexKind::Bound(debruijn), ty) => {1680 write!(f, "?{}.{}", debruijn.as_usize(), ty.var.as_usize())?1681 }1682 TyKind::Bound(BoundVarIndexKind::Canonical, ty) => {1683 write!(f, "?c.{}", ty.var.as_usize())?1684 }1685 TyKind::Dynamic(bounds, region) => {1686 // We want to put auto traits after principal traits, regardless of their written order.1687 let mut bounds_to_display = SmallVec::<[_; 4]>::new();1688 let mut auto_trait_bounds = SmallVec::<[_; 4]>::new();1689 for bound in bounds.iter() {1690 let clause = bound.with_self_ty(interner, *self);1691 match bound.skip_binder() {1692 ExistentialPredicate::Trait(_) | ExistentialPredicate::Projection(_) => {1693 bounds_to_display.push(clause);1694 }1695 ExistentialPredicate::AutoTrait(_) => auto_trait_bounds.push(clause),1696 }1697 }1698 bounds_to_display.append(&mut auto_trait_bounds);16991700 if f.render_region(region) {1701 bounds_to_display1702 .push(rustc_type_ir::OutlivesPredicate(*self, region).upcast(interner));1703 }17041705 write_bounds_like_dyn_trait_with_prefix(1706 f,1707 "dyn",1708 Either::Left(*self),1709 &bounds_to_display,1710 SizedByDefault::NotSized,1711 trait_bounds_need_parens,1712 )?;1713 }1714 TyKind::Error(_) => {1715 if f.display_kind.is_source_code() {1716 f.write_char('_')?;1717 } else {1718 write!(f, "{{unknown}}")?;1719 }1720 }1721 TyKind::Infer(..) => write!(f, "_")?,1722 TyKind::Coroutine(coroutine_id, subst) => {1723 let kind = coroutine_id.0.loc(db).kind;1724 let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } =1725 subst.split_coroutine_args();1726 match kind {1727 HirClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => {1728 let lang_items = f.lang_items();1729 let future_trait = lang_items.Future;1730 let output = lang_items.FutureOutput;1731 write!(f, "impl ")?;1732 if let Some(t) = future_trait {1733 f.start_location_link(t.into());1734 }1735 write!(f, "Future")?;1736 if future_trait.is_some() {1737 f.end_location_link();1738 }1739 write!(f, "<")?;1740 if let Some(t) = output {1741 f.start_location_link(t.into());1742 }1743 write!(f, "Output")?;1744 if output.is_some() {1745 f.end_location_link();1746 }1747 write!(f, " = ")?;1748 return_ty.hir_fmt(f)?;1749 write!(f, ">")?;1750 }1751 HirClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => {1752 let lang_items = f.lang_items();1753 let iterator_trait = lang_items.Iterator;1754 let item = lang_items.IteratorItem;1755 write!(f, "impl ")?;1756 if let Some(t) = iterator_trait {1757 f.start_location_link(t.into());1758 }1759 write!(f, "Iterator")?;1760 if iterator_trait.is_some() {1761 f.end_location_link();1762 }1763 write!(f, "<")?;1764 if let Some(t) = item {1765 f.start_location_link(t.into());1766 }1767 write!(f, "Item")?;1768 if item.is_some() {1769 f.end_location_link();1770 }1771 write!(f, " = ")?;1772 yield_ty.hir_fmt(f)?;1773 write!(f, ">")?;1774 }1775 HirClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => {1776 let lang_items = f.lang_items();1777 let async_iterator_trait = lang_items.AsyncIterator;1778 let item = lang_items.AsyncIteratorItem;1779 write!(f, "impl ")?;1780 if let Some(t) = async_iterator_trait {1781 f.start_location_link(t.into());1782 }1783 write!(f, "AsyncIterator")?;1784 if async_iterator_trait.is_some() {1785 f.end_location_link();1786 }1787 write!(f, "<")?;1788 if let Some(t) = item {1789 f.start_location_link(t.into());1790 }1791 write!(f, "Item")?;1792 if item.is_some() {1793 f.end_location_link();1794 }1795 write!(f, " = ")?;1796 let item_ty = async_gen_item_ty_from_yield_ty(f.lang_items(), yield_ty)1797 .unwrap_or(yield_ty);1798 item_ty.hir_fmt(f)?;1799 write!(f, ">")?;1800 }1801 HirClosureKind::OldCoroutine(..) => {1802 if f.display_kind.is_source_code() {1803 return Err(HirDisplayError::DisplaySourceCodeError(1804 DisplaySourceCodeError::Coroutine,1805 ));1806 }1807 write!(f, "|")?;1808 resume_ty.hir_fmt(f)?;1809 write!(f, "|")?;18101811 write!(f, " yields ")?;1812 yield_ty.hir_fmt(f)?;18131814 write!(f, " -> ")?;1815 return_ty.hir_fmt(f)?;1816 }1817 _ => panic!("invalid kind for coroutine: {kind:?}"),1818 }1819 }1820 TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?,1821 TyKind::Pat(_, _) => write!(f, "{{pat}}")?,1822 TyKind::UnsafeBinder(_) => write!(f, "{{unsafe binder}}")?,1823 TyKind::Alias(..) => write!(f, "{{alias}}")?,1824 }1825 Ok(())1826 }1827}18281829fn hir_fmt_generics<'db>(1830 f: &mut HirFormatter<'_, 'db>,1831 parameters: &[GenericArg<'db>],1832 generic_def: Option<hir_def::GenericDefId>,1833 self_: Option<Ty<'db>>,1834) -> Result {1835 if parameters.is_empty() {1836 return Ok(());1837 }18381839 let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters);18401841 if !parameters_to_write.is_empty() {1842 write!(f, "<")?;1843 hir_fmt_generic_arguments(f, parameters_to_write, self_)?;1844 write!(f, ">")?;1845 }18461847 Ok(())1848}18491850fn generic_args_sans_defaults<'ga, 'db>(1851 f: &mut HirFormatter<'_, 'db>,1852 generic_def: Option<hir_def::GenericDefId>,1853 parameters: &'ga [GenericArg<'db>],1854) -> &'ga [GenericArg<'db>] {1855 if f.display_kind.is_source_code() || f.omit_verbose_types() {1856 match generic_def.map(|generic_def_id| f.db.generic_defaults(generic_def_id)) {1857 None => parameters,1858 Some(default_parameters) => {1859 let should_show = |arg: GenericArg<'db>, i: usize| match default_parameters.get(i) {1860 None => true,1861 Some(default_parameter) => {1862 arg != default_parameter1863 .instantiate(f.interner, ¶meters[..i])1864 .skip_norm_wip()1865 }1866 };1867 let mut default_from = 0;1868 for (i, ¶meter) in parameters.iter().enumerate() {1869 if should_show(parameter, i) {1870 default_from = i + 1;1871 }1872 }1873 ¶meters[0..default_from]1874 }1875 }1876 } else {1877 parameters1878 }1879}18801881fn hir_fmt_generic_args<'db>(1882 f: &mut HirFormatter<'_, 'db>,1883 parameters: &[GenericArg<'db>],1884 generic_def: Option<hir_def::GenericDefId>,1885 self_: Option<Ty<'db>>,1886) -> Result {1887 if parameters.is_empty() {1888 return Ok(());1889 }18901891 let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters);18921893 if !parameters_to_write.is_empty() {1894 write!(f, "<")?;1895 hir_fmt_generic_arguments(f, parameters_to_write, self_)?;1896 write!(f, ">")?;1897 }18981899 Ok(())1900}19011902fn hir_fmt_generic_arguments<'db>(1903 f: &mut HirFormatter<'_, 'db>,1904 parameters: &[GenericArg<'db>],1905 self_: Option<Ty<'db>>,1906) -> Result {1907 let mut first = true;1908 let lifetime_offset = parameters.iter().position(|arg| arg.region().is_some());19091910 let (ty_or_const, lifetimes) = match lifetime_offset {1911 Some(offset) => parameters.split_at(offset),1912 None => (parameters, &[][..]),1913 };1914 for generic_arg in lifetimes.iter().chain(ty_or_const) {1915 if !mem::take(&mut first) {1916 write!(f, ", ")?;1917 }1918 match self_ {1919 self_ @ Some(_) if generic_arg.ty() == self_ => write!(f, "Self")?,1920 _ => generic_arg.hir_fmt(f)?,1921 }1922 }1923 Ok(())1924}19251926fn hir_fmt_tys<'db>(1927 f: &mut HirFormatter<'_, 'db>,1928 tys: &[Ty<'db>],1929 self_: Option<Ty<'db>>,1930) -> Result {1931 let mut first = true;19321933 for ty in tys {1934 if !mem::take(&mut first) {1935 write!(f, ", ")?;1936 }1937 match self_ {1938 Some(self_) if *ty == self_ => write!(f, "Self")?,1939 _ => ty.hir_fmt(f)?,1940 }1941 }1942 Ok(())1943}19441945impl<'db> HirDisplay<'db> for PolyFnSig<'db> {1946 fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {1947 let FnSig { inputs_and_output, fn_sig_kind } = self.skip_binder();1948 if let Safety::Unsafe = fn_sig_kind.safety() {1949 write!(f, "unsafe ")?;1950 }1951 // FIXME: Enable this when the FIXME on FnAbi regarding PartialEq is fixed.1952 // if !matches!(abi, FnAbi::Rust) {1953 // f.write_str("extern \"")?;1954 // f.write_str(abi.as_str())?;1955 // f.write_str("\" ")?;1956 // }1957 write!(f, "fn(")?;1958 f.write_joined(inputs_and_output.inputs(), ", ")?;1959 if fn_sig_kind.c_variadic() {1960 if inputs_and_output.inputs().is_empty() {1961 write!(f, "...")?;1962 } else {1963 write!(f, ", ...")?;1964 }1965 }1966 write!(f, ")")?;1967 let ret = inputs_and_output.output();1968 if !ret.is_unit() {1969 write!(f, " -> ")?;1970 ret.hir_fmt(f)?;1971 }1972 Ok(())1973 }1974}19751976impl<'db> HirDisplay<'db> for Term<'db> {1977 fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {1978 match self.kind() {1979 TermKind::Ty(it) => it.hir_fmt(f),1980 TermKind::Const(it) => it.hir_fmt(f),1981 }1982 }1983}19841985#[derive(Clone, Copy, PartialEq, Eq)]1986pub enum SizedByDefault {1987 NotSized,1988 Sized { anchor: Crate },1989}19901991impl SizedByDefault {1992 fn is_sized_trait(self, trait_: TraitId, interner: DbInterner<'_>) -> bool {1993 match self {1994 Self::NotSized => false,1995 Self::Sized { .. } => {1996 let sized_trait = interner.lang_items().Sized;1997 Some(trait_) == sized_trait1998 }1999 }2000 }
Findings
✓ No findings reported for this file.