X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=crates%2Fhir_ty%2Fsrc%2Fdisplay.rs;h=2ee4f5cf41bf351508ab8bf3143c1bd1f4bde481;hb=d1fc208c9ceb959d616fa790fca5d282bc8d820d;hp=3f7455959d763779e5311901c95805111f643c8f;hpb=cae54d86d8ad0857c1ef960c06883ee92e0ba4c0;p=rust.git diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 3f7455959d7..2ee4f5cf41b 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -4,6 +4,7 @@ use std::fmt::{self, Debug}; +use base_db::CrateId; use chalk_ir::BoundVar; use hir_def::{ body, @@ -13,12 +14,13 @@ intern::{Internable, Interned}, item_scope::ItemInNs, path::{Path, PathKind}, - type_ref::{TypeBound, TypeRef}, + type_ref::{TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - AssocContainerId, Lookup, ModuleId, TraitId, + HasModule, ItemContainerId, Lookup, ModuleId, TraitId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use itertools::Itertools; +use syntax::SmolStr; use crate::{ const_from_placeholder_idx, @@ -149,6 +151,12 @@ pub fn write_joined( write!(self, "{}", sep)?; } first = false; + + // Abbreviate multiple omitted types with a single ellipsis. + if self.should_truncate() { + return write!(self, "{}", TYPE_HINT_TRUNCATION); + } + e.hir_fmt(self)?; } Ok(()) @@ -166,10 +174,9 @@ pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> } pub fn should_truncate(&self) -> bool { - if let Some(max_size) = self.max_size { - self.curr_size >= max_size - } else { - false + match self.max_size { + Some(max_size) => self.curr_size >= max_size, + None => false, } } @@ -273,11 +280,11 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { let trait_ = f.db.trait_data(self.trait_(f.db)); write!(f, "<")?; - self.self_type_parameter(&Interner).hir_fmt(f)?; + self.self_type_parameter(Interner).hir_fmt(f)?; write!(f, " as {}", trait_.name)?; - if self.substitution.len(&Interner) > 1 { + if self.substitution.len(Interner) > 1 { write!(f, "<")?; - f.write_joined(&self.substitution.as_slice(&Interner)[1..], ", ")?; + f.write_joined(&self.substitution.as_slice(Interner)[1..], ", ")?; write!(f, ">")?; } write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?; @@ -291,7 +298,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { return write!(f, "{}", TYPE_HINT_TRUNCATION); } - self.substitution.at(&Interner, 0).hir_fmt(f) + self.substitution.at(Interner, 0).hir_fmt(f) } } @@ -334,7 +341,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { return write!(f, "{}", TYPE_HINT_TRUNCATION); } - match self.kind(&Interner) { + match self.kind(Interner) { TyKind::Never => write!(f, "!")?, TyKind::Str => write!(f, "str")?, TyKind::Scalar(Scalar::Bool) => write!(f, "bool")?, @@ -355,7 +362,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { write!(f, "]")?; } TyKind::Raw(m, t) | TyKind::Ref(m, _, t) => { - if matches!(self.kind(&Interner), TyKind::Raw(..)) { + if matches!(self.kind(Interner), TyKind::Raw(..)) { write!( f, "*{}", @@ -376,10 +383,20 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { } // FIXME: all this just to decide whether to use parentheses... - let datas; - let predicates: Vec<_> = match t.kind(&Interner) { + let contains_impl_fn = |bounds: &[QuantifiedWhereClause]| { + bounds.iter().any(|bound| { + if let WhereClause::Implemented(trait_ref) = bound.skip_binders() { + let trait_ = trait_ref.hir_trait_id(); + fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) + } else { + false + } + }) + }; + let (preds_to_print, has_impl_fn_pred) = match t.kind(Interner) { TyKind::Dyn(dyn_ty) if dyn_ty.bounds.skip_binders().interned().len() > 1 => { - dyn_ty.bounds.skip_binders().interned().iter().cloned().collect() + let bounds = dyn_ty.bounds.skip_binders().interned(); + (bounds.len(), contains_impl_fn(bounds)) } TyKind::Alias(AliasTy::Opaque(OpaqueTy { opaque_ty_id, @@ -389,33 +406,54 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { let impl_trait_id = f.db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id { - datas = + let datas = f.db.return_type_impl_traits(func) .expect("impl trait id without data"); let data = (*datas) .as_ref() .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); - let bounds = data.substitute(&Interner, parameters); - bounds.into_value_and_skipped_binders().0 + let bounds = data.substitute(Interner, parameters); + let mut len = bounds.skip_binders().len(); + + // Don't count Sized but count when it absent + // (i.e. when explicit ?Sized bound is set). + let default_sized = SizedByDefault::Sized { + anchor: func.lookup(f.db.upcast()).module(f.db.upcast()).krate(), + }; + let sized_bounds = bounds + .skip_binders() + .iter() + .filter(|b| { + matches!( + b.skip_binders(), + WhereClause::Implemented(trait_ref) + if default_sized.is_sized_trait( + trait_ref.hir_trait_id(), + f.db.upcast(), + ), + ) + }) + .count(); + match sized_bounds { + 0 => len += 1, + _ => { + len = len.saturating_sub(sized_bounds); + } + } + + (len, contains_impl_fn(bounds.skip_binders())) } else { - Vec::new() + (0, false) } } - _ => Vec::new(), + _ => (0, false), }; - if let Some(WhereClause::Implemented(trait_ref)) = - predicates.get(0).map(|b| b.skip_binders()) - { - let trait_ = trait_ref.hir_trait_id(); - if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) - && predicates.len() <= 2 - { - return t.hir_fmt(f); - } + if has_impl_fn_pred && preds_to_print <= 2 { + return t.hir_fmt(f); } - if predicates.len() > 1 { + if preds_to_print > 1 { write!(f, "(")?; t.hir_fmt(f)?; write!(f, ")")?; @@ -424,13 +462,13 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { } } TyKind::Tuple(_, substs) => { - if substs.len(&Interner) == 1 { + if substs.len(Interner) == 1 { write!(f, "(")?; - substs.at(&Interner, 0).hir_fmt(f)?; + substs.at(Interner, 0).hir_fmt(f)?; write!(f, ",)")?; } else { write!(f, "(")?; - f.write_joined(&*substs.as_slice(&Interner), ", ")?; + f.write_joined(&*substs.as_slice(Interner), ", ")?; write!(f, ")")?; } } @@ -440,7 +478,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { } TyKind::FnDef(def, parameters) => { let def = from_chalk(f.db, *def); - let sig = f.db.callable_item_signature(def).substitute(&Interner, parameters); + let sig = f.db.callable_item_signature(def).substitute(Interner, parameters); match def { CallableDefId::FunctionId(ff) => { write!(f, "fn {}", f.db.function_data(ff).name)? @@ -450,7 +488,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)? } }; - if parameters.len(&Interner) > 0 { + if parameters.len(Interner) > 0 { let generics = generics(f.db.upcast(), def.into()); let (parent_params, self_param, type_params, _impl_trait_params) = generics.provenance_split(); @@ -458,7 +496,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? if total_len > 0 { write!(f, "<")?; - f.write_joined(¶meters.as_slice(&Interner)[..total_len], ", ")?; + f.write_joined(¶meters.as_slice(Interner)[..total_len], ", ")?; write!(f, ">")?; } } @@ -496,7 +534,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { } } - if parameters.len(&Interner) > 0 { + if parameters.len(Interner) > 0 { let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() { @@ -505,39 +543,57 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) .filter(|defaults| !defaults.is_empty()) { - None => parameters.as_slice(&Interner), + None => parameters.as_slice(Interner), Some(default_parameters) => { let mut default_from = 0; - for (i, parameter) in parameters.iter(&Interner).enumerate() { + for (i, parameter) in parameters.iter(Interner).enumerate() { match ( - parameter.assert_ty_ref(&Interner).kind(&Interner), + parameter.assert_ty_ref(Interner).kind(Interner), default_parameters.get(i), ) { (&TyKind::Error, _) | (_, None) => { default_from = i + 1; } (_, Some(default_parameter)) => { - let actual_default = - default_parameter.clone().substitute( - &Interner, - &subst_prefix(parameters, i), - ); - if parameter.assert_ty_ref(&Interner) != &actual_default + let actual_default = default_parameter + .clone() + .substitute(Interner, &subst_prefix(parameters, i)); + if parameter.assert_ty_ref(Interner) != &actual_default { default_from = i + 1; } } } } - ¶meters.as_slice(&Interner)[0..default_from] + ¶meters.as_slice(Interner)[0..default_from] } } } else { - parameters.as_slice(&Interner) + parameters.as_slice(Interner) }; if !parameters_to_write.is_empty() { write!(f, "<")?; - f.write_joined(parameters_to_write, ", ")?; + + if f.display_target.is_source_code() { + let mut first = true; + for generic_arg in parameters_to_write { + if !first { + write!(f, ", ")?; + } + first = false; + + if generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) + == Some(&TyKind::Error) + { + write!(f, "_")?; + } else { + generic_arg.hir_fmt(f)?; + } + } + } else { + f.write_joined(parameters_to_write, ", ")?; + } + write!(f, ">")?; } } @@ -545,7 +601,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { TyKind::AssociatedType(assoc_type_id, parameters) => { let type_alias = from_assoc_type_id(*assoc_type_id); let trait_ = match type_alias.lookup(f.db.upcast()).container { - AssocContainerId::TraitId(it) => it, + ItemContainerId::TraitId(it) => it, _ => panic!("not an associated type"), }; let trait_ = f.db.trait_data(trait_); @@ -554,9 +610,9 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types) if f.display_target.is_test() { write!(f, "{}::{}", trait_.name, type_alias_data.name)?; - if parameters.len(&Interner) > 0 { + if parameters.len(Interner) > 0 { write!(f, "<")?; - f.write_joined(&*parameters.as_slice(&Interner), ", ")?; + f.write_joined(&*parameters.as_slice(Interner), ", ")?; write!(f, ">")?; } } else { @@ -581,13 +637,19 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { let data = (*datas) .as_ref() .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); - let bounds = data.substitute(&Interner, ¶meters); - write_bounds_like_dyn_trait_with_prefix("impl", bounds.skip_binders(), f)?; + let bounds = data.substitute(Interner, ¶meters); + let krate = func.lookup(f.db.upcast()).module(f.db.upcast()).krate(); + write_bounds_like_dyn_trait_with_prefix( + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + f, + )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "impl Future")?; } } @@ -598,11 +660,11 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { DisplaySourceCodeError::Closure, )); } - let sig = substs.at(&Interner, 0).assert_ty_ref(&Interner).callable_sig(f.db); + let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(f.db); if let Some(sig) = sig { if sig.params().is_empty() { write!(f, "||")?; - } else if f.omit_verbose_types() { + } else if f.should_truncate() { write!(f, "|{}|", TYPE_HINT_TRUNCATION)?; } else { write!(f, "|")?; @@ -628,20 +690,26 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { let substs = generics.type_params_subst(f.db); let bounds = f.db.generic_predicates(id.parent) - .into_iter() - .map(|pred| pred.clone().substitute(&Interner, &substs)) + .iter() + .map(|pred| pred.clone().substitute(Interner, &substs)) .filter(|wc| match &wc.skip_binders() { WhereClause::Implemented(tr) => { - &tr.self_type_parameter(&Interner) == self + &tr.self_type_parameter(Interner) == self } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), ty: _, - }) => &proj.self_type_parameter(&Interner) == self, + }) => &proj.self_type_parameter(Interner) == self, _ => false, }) .collect::>(); - write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?; + let krate = id.parent.module(f.db.upcast()).krate(); + write_bounds_like_dyn_trait_with_prefix( + "impl", + &bounds, + SizedByDefault::Sized { anchor: krate }, + f, + )?; } } } @@ -650,6 +718,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { write_bounds_like_dyn_trait_with_prefix( "dyn", dyn_ty.bounds.skip_binders().interned(), + SizedByDefault::NotSized, f, )?; } @@ -663,8 +732,14 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { let data = (*datas) .as_ref() .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); - let bounds = data.substitute(&Interner, &opaque_ty.substitution); - write_bounds_like_dyn_trait_with_prefix("impl", bounds.skip_binders(), f)?; + let bounds = data.substitute(Interner, &opaque_ty.substitution); + let krate = func.lookup(f.db.upcast()).module(f.db.upcast()).krate(); + write_bounds_like_dyn_trait_with_prefix( + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + f, + )?; } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; @@ -713,15 +788,38 @@ fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator bool { + match self { + Self::NotSized => false, + Self::Sized { anchor } => { + let sized_trait = db + .lang_item(anchor, SmolStr::new_inline("sized")) + .and_then(|lang_item| lang_item.as_trait()); + Some(trait_) == sized_trait + } + } + } +} + pub fn write_bounds_like_dyn_trait_with_prefix( prefix: &str, predicates: &[QuantifiedWhereClause], + default_sized: SizedByDefault, f: &mut HirFormatter, ) -> Result<(), HirDisplayError> { write!(f, "{}", prefix)?; - if !predicates.is_empty() { + if !predicates.is_empty() + || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. }) + { write!(f, " ")?; - write_bounds_like_dyn_trait(predicates, f) + write_bounds_like_dyn_trait(predicates, default_sized, f) } else { Ok(()) } @@ -729,6 +827,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix( fn write_bounds_like_dyn_trait( predicates: &[QuantifiedWhereClause], + default_sized: SizedByDefault, f: &mut HirFormatter, ) -> Result<(), HirDisplayError> { // Note: This code is written to produce nice results (i.e. @@ -740,10 +839,18 @@ fn write_bounds_like_dyn_trait( let mut first = true; let mut angle_open = false; let mut is_fn_trait = false; + let mut is_sized = false; for p in predicates.iter() { match p.skip_binders() { WhereClause::Implemented(trait_ref) => { let trait_ = trait_ref.hir_trait_id(); + if default_sized.is_sized_trait(trait_, f.db.upcast()) { + is_sized = true; + if matches!(default_sized, SizedByDefault::Sized { .. }) { + // Don't print +Sized, but rather +?Sized if absent. + continue; + } + } if !is_fn_trait { is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_); } @@ -758,13 +865,13 @@ fn write_bounds_like_dyn_trait( // existential) here, which is the only thing that's // possible in actual Rust, and hence don't print it write!(f, "{}", f.db.trait_data(trait_).name)?; - if let [_, params @ ..] = &*trait_ref.substitution.as_slice(&Interner) { + if let [_, params @ ..] = &*trait_ref.substitution.as_slice(Interner) { if is_fn_trait { if let Some(args) = - params.first().and_then(|it| it.assert_ty_ref(&Interner).as_tuple()) + params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple()) { write!(f, "(")?; - f.write_joined(args.as_slice(&Interner), ", ")?; + f.write_joined(args.as_slice(Interner), ", ")?; write!(f, ")")?; } } else if !params.is_empty() { @@ -808,6 +915,13 @@ fn write_bounds_like_dyn_trait( if angle_open { write!(f, ">")?; } + if matches!(default_sized, SizedByDefault::Sized { .. }) { + if !is_sized { + write!(f, "{}?Sized", if first { "" } else { " + " })?; + } else if first { + write!(f, "Sized")?; + } + } Ok(()) } @@ -816,16 +930,16 @@ fn fmt_trait_ref(tr: &TraitRef, f: &mut HirFormatter, use_as: bool) -> Result<() return write!(f, "{}", TYPE_HINT_TRUNCATION); } - tr.self_type_parameter(&Interner).hir_fmt(f)?; + tr.self_type_parameter(Interner).hir_fmt(f)?; if use_as { write!(f, " as ")?; } else { write!(f, ": ")?; } write!(f, "{}", f.db.trait_data(tr.hir_trait_id()).name)?; - if tr.substitution.len(&Interner) > 1 { + if tr.substitution.len(Interner) > 1 { write!(f, "<")?; - f.write_joined(&tr.substitution.as_slice(&Interner)[1..], ", ")?; + f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?; write!(f, ">")?; } Ok(()) @@ -980,20 +1094,33 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { inner.hir_fmt(f)?; write!(f, "]")?; } - TypeRef::Fn(tys, is_varargs) => { + TypeRef::Fn(parameters, is_varargs) => { // FIXME: Function pointer qualifiers. write!(f, "fn(")?; - f.write_joined(&tys[..tys.len() - 1], ", ")?; + for index in 0..parameters.len() - 1 { + let (param_name, param_type) = ¶meters[index]; + if let Some(name) = param_name { + write!(f, "{}: ", name)?; + } + + param_type.hir_fmt(f)?; + + // Last index contains the return type so we stop writing commas on the second-to-last index + if index != parameters.len() - 2 { + write!(f, ", ")?; + } + } if *is_varargs { - write!(f, "{}...", if tys.len() == 1 { "" } else { ", " })?; + write!(f, "{}...", if parameters.len() == 1 { "" } else { ", " })?; } write!(f, ")")?; - let ret_ty = tys.last().unwrap(); - match ret_ty { - TypeRef::Tuple(tup) if tup.is_empty() => {} - _ => { - write!(f, " -> ")?; - ret_ty.hir_fmt(f)?; + if let Some((_, ret_ty)) = ¶meters.last() { + match ret_ty { + TypeRef::Tuple(tup) if tup.is_empty() => {} + _ => { + write!(f, " -> ")?; + ret_ty.hir_fmt(f)?; + } } } } @@ -1026,7 +1153,13 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { impl HirDisplay for TypeBound { fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { match self { - TypeBound::Path(path) => path.hir_fmt(f), + TypeBound::Path(path, modifier) => { + match modifier { + TraitBoundModifier::None => (), + TraitBoundModifier::Maybe => write!(f, "?")?, + } + path.hir_fmt(f) + } TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name), TypeBound::ForLifetime(lifetimes, path) => { write!(f, "for<{}> ", lifetimes.iter().format(", "))?; @@ -1046,26 +1179,51 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { write!(f, ">")?; } (_, PathKind::Plain) => {} - (_, PathKind::Abs) => write!(f, "::")?, + (_, PathKind::Abs) => {} (_, PathKind::Crate) => write!(f, "crate")?, (_, PathKind::Super(0)) => write!(f, "self")?, (_, PathKind::Super(n)) => { - write!(f, "super")?; - for _ in 0..*n { - write!(f, "::super")?; + for i in 0..*n { + if i > 0 { + write!(f, "::")?; + } + write!(f, "super")?; } } (_, PathKind::DollarCrate(_)) => write!(f, "{{extern_crate}}")?, } for (seg_idx, segment) in self.segments().iter().enumerate() { - if seg_idx != 0 { + if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 { write!(f, "::")?; } write!(f, "{}", segment.name)?; if let Some(generic_args) = segment.args_and_bindings { // We should be in type context, so format as `Foo` instead of `Foo::`. // Do we actually format expressions? + if generic_args.desugared_from_fn { + // First argument will be a tuple, which already includes the parentheses. + // If the tuple only contains 1 item, write it manually to avoid the trailing `,`. + if let hir_def::path::GenericArg::Type(TypeRef::Tuple(v)) = + &generic_args.args[0] + { + if v.len() == 1 { + write!(f, "(")?; + v[0].hir_fmt(f)?; + write!(f, ")")?; + } else { + generic_args.args[0].hir_fmt(f)?; + } + } + if let Some(ret) = &generic_args.bindings[0].type_ref { + if !matches!(ret, TypeRef::Tuple(v) if v.is_empty()) { + write!(f, " -> ")?; + ret.hir_fmt(f)?; + } + } + return Ok(()); + } + write!(f, "<")?; let mut first = true; for arg in &generic_args.args {