Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/display.rs199
-rw-r--r--crates/hir-ty/src/tests/coercion.rs2
-rw-r--r--crates/hir-ty/src/tests/display_source_code.rs4
-rw-r--r--crates/hir-ty/src/tests/traits.rs2
-rw-r--r--crates/hir/src/display.rs2
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs17
6 files changed, 118 insertions, 108 deletions
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 43b428c3fa..4e77e8be36 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -12,7 +12,6 @@ use either::Either;
use hir_def::{
FindPathConfig, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, ModuleDefId,
ModuleId, TraitId,
- db::DefDatabase,
expr_store::{ExpressionStore, path::Path},
find_path::{self, PrefixKind},
hir::generics::{TypeOrConstParamData, TypeParamProvenance, WherePredicate},
@@ -100,6 +99,9 @@ pub struct HirFormatter<'a, 'db> {
display_kind: DisplayKind,
display_target: DisplayTarget,
bounds_formatting_ctx: BoundsFormattingCtx<'db>,
+ /// Whether formatting `impl Trait1 + Trait2` or `dyn Trait1 + Trait2` needs parentheses around it,
+ /// for example when formatting `&(impl Trait1 + Trait2)`.
+ trait_bounds_need_parens: bool,
}
// FIXME: To consider, ref and dyn trait lifetimes can be omitted if they are `'_`, path args should
@@ -331,6 +333,7 @@ pub trait HirDisplay<'db> {
show_container_bounds: false,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
bounds_formatting_ctx: Default::default(),
+ trait_bounds_need_parens: false,
}) {
Ok(()) => {}
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -566,6 +569,7 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> {
show_container_bounds: self.show_container_bounds,
display_lifetimes: self.display_lifetimes,
bounds_formatting_ctx: Default::default(),
+ trait_bounds_need_parens: false,
})
}
@@ -612,7 +616,11 @@ impl<'db, T: HirDisplay<'db> + Internable> HirDisplay<'db> for Interned<T> {
}
}
-fn write_projection<'db>(f: &mut HirFormatter<'_, 'db>, alias: &AliasTy<'db>) -> Result {
+fn write_projection<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ alias: &AliasTy<'db>,
+ needs_parens_if_multi: bool,
+) -> Result {
if f.should_truncate() {
return write!(f, "{TYPE_HINT_TRUNCATION}");
}
@@ -650,6 +658,7 @@ fn write_projection<'db>(f: &mut HirFormatter<'_, 'db>, alias: &AliasTy<'db>) ->
Either::Left(Ty::new_alias(f.interner, AliasTyKind::Projection, *alias)),
&bounds,
SizedByDefault::NotSized,
+ needs_parens_if_multi,
)
});
}
@@ -1056,7 +1065,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
return write!(f, "{TYPE_HINT_TRUNCATION}");
}
- use TyKind;
+ let trait_bounds_need_parens = mem::replace(&mut f.trait_bounds_need_parens, false);
match self.kind() {
TyKind::Never => write!(f, "!")?,
TyKind::Str => write!(f, "str")?,
@@ -1077,103 +1086,34 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
c.hir_fmt(f)?;
write!(f, "]")?;
}
- kind @ (TyKind::RawPtr(t, m) | TyKind::Ref(_, t, m)) => {
- if let TyKind::Ref(l, _, _) = kind {
- f.write_char('&')?;
- if f.render_region(l) {
- l.hir_fmt(f)?;
- f.write_char(' ')?;
- }
- match m {
- rustc_ast_ir::Mutability::Not => (),
- rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?,
- }
- } else {
- write!(
- f,
- "*{}",
- match m {
- rustc_ast_ir::Mutability::Not => "const ",
- rustc_ast_ir::Mutability::Mut => "mut ",
- }
- )?;
+ TyKind::Ref(l, t, m) => {
+ f.write_char('&')?;
+ if f.render_region(l) {
+ l.hir_fmt(f)?;
+ f.write_char(' ')?;
+ }
+ match m {
+ rustc_ast_ir::Mutability::Not => (),
+ rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?,
}
- // FIXME: all this just to decide whether to use parentheses...
- let (preds_to_print, has_impl_fn_pred) = match t.kind() {
- TyKind::Dynamic(bounds, region) => {
- let contains_impl_fn =
- bounds.iter().any(|bound| match bound.skip_binder() {
- ExistentialPredicate::Trait(trait_ref) => {
- let trait_ = trait_ref.def_id.0;
- fn_traits(f.lang_items()).any(|it| it == trait_)
- }
- _ => false,
- });
- let render_lifetime = f.render_region(region);
- (bounds.len() + render_lifetime as usize, contains_impl_fn)
- }
- TyKind::Alias(AliasTyKind::Opaque, ty) => {
- let opaque_ty_id = match ty.def_id {
- SolverDefId::InternedOpaqueTyId(id) => id,
- _ => unreachable!(),
- };
- let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id);
- if let ImplTraitId::ReturnTypeImplTrait(func, _) = impl_trait_id {
- let data = impl_trait_id.predicates(db);
- let bounds =
- || data.iter_instantiated_copied(f.interner, ty.args.as_slice());
- let mut len = bounds().count();
-
- // Don't count Sized but count when it absent
- // (i.e. when explicit ?Sized bound is set).
- let default_sized = SizedByDefault::Sized { anchor: func.krate(db) };
- let sized_bounds = bounds()
- .filter(|b| {
- matches!(
- b.kind().skip_binder(),
- ClauseKind::Trait(trait_ref)
- if default_sized.is_sized_trait(
- trait_ref.def_id().0,
- db,
- ),
- )
- })
- .count();
- match sized_bounds {
- 0 => len += 1,
- _ => {
- len = len.saturating_sub(sized_bounds);
- }
- }
-
- let contains_impl_fn = bounds().any(|bound| {
- if let ClauseKind::Trait(trait_ref) = bound.kind().skip_binder() {
- let trait_ = trait_ref.def_id().0;
- fn_traits(f.lang_items()).any(|it| it == trait_)
- } else {
- false
- }
- });
- (len, contains_impl_fn)
- } else {
- (0, false)
- }
+ f.trait_bounds_need_parens = true;
+ t.hir_fmt(f)?;
+ f.trait_bounds_need_parens = false;
+ }
+ TyKind::RawPtr(t, m) => {
+ write!(
+ f,
+ "*{}",
+ match m {
+ rustc_ast_ir::Mutability::Not => "const ",
+ rustc_ast_ir::Mutability::Mut => "mut ",
}
- _ => (0, false),
- };
-
- if has_impl_fn_pred && preds_to_print <= 2 {
- return t.hir_fmt(f);
- }
+ )?;
- if preds_to_print > 1 {
- write!(f, "(")?;
- t.hir_fmt(f)?;
- write!(f, ")")?;
- } else {
- t.hir_fmt(f)?;
- }
+ f.trait_bounds_need_parens = true;
+ t.hir_fmt(f)?;
+ f.trait_bounds_need_parens = false;
}
TyKind::Tuple(tys) => {
if tys.len() == 1 {
@@ -1328,7 +1268,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?;
}
- TyKind::Alias(AliasTyKind::Projection, alias_ty) => write_projection(f, &alias_ty)?,
+ TyKind::Alias(AliasTyKind::Projection, alias_ty) => {
+ write_projection(f, &alias_ty, trait_bounds_need_parens)?
+ }
TyKind::Foreign(alias) => {
let type_alias = db.type_alias_signature(alias.0);
f.start_location_link(alias.0.into());
@@ -1363,6 +1305,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
Either::Left(*self),
&bounds,
SizedByDefault::Sized { anchor: krate },
+ trait_bounds_need_parens,
)?;
}
TyKind::Closure(id, substs) => {
@@ -1525,6 +1468,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
Either::Left(*self),
&bounds,
SizedByDefault::Sized { anchor: krate },
+ trait_bounds_need_parens,
)?;
}
},
@@ -1567,6 +1511,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
Either::Left(*self),
&bounds_to_display,
SizedByDefault::NotSized,
+ trait_bounds_need_parens,
)?;
}
TyKind::Error(_) => {
@@ -1806,11 +1751,11 @@ pub enum SizedByDefault {
}
impl SizedByDefault {
- fn is_sized_trait(self, trait_: TraitId, db: &dyn DefDatabase) -> bool {
+ fn is_sized_trait(self, trait_: TraitId, interner: DbInterner<'_>) -> bool {
match self {
Self::NotSized => false,
- Self::Sized { anchor } => {
- let sized_trait = hir_def::lang_item::lang_items(db, anchor).Sized;
+ Self::Sized { .. } => {
+ let sized_trait = interner.lang_items().Sized;
Some(trait_) == sized_trait
}
}
@@ -1823,16 +1768,62 @@ pub fn write_bounds_like_dyn_trait_with_prefix<'db>(
this: Either<Ty<'db>, Region<'db>>,
predicates: &[Clause<'db>],
default_sized: SizedByDefault,
+ needs_parens_if_multi: bool,
) -> Result {
+ let needs_parens =
+ needs_parens_if_multi && trait_bounds_need_parens(f, this, predicates, default_sized);
+ if needs_parens {
+ write!(f, "(")?;
+ }
write!(f, "{prefix}")?;
if !predicates.is_empty()
|| predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. })
{
write!(f, " ")?;
- write_bounds_like_dyn_trait(f, this, predicates, default_sized)
- } else {
- Ok(())
+ write_bounds_like_dyn_trait(f, this, predicates, default_sized)?;
+ }
+ if needs_parens {
+ write!(f, ")")?;
+ }
+ Ok(())
+}
+
+fn trait_bounds_need_parens<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ this: Either<Ty<'db>, Region<'db>>,
+ predicates: &[Clause<'db>],
+ default_sized: SizedByDefault,
+) -> bool {
+ // This needs to be kept in sync with `write_bounds_like_dyn_trait()`.
+ let mut distinct_bounds = 0usize;
+ let mut is_sized = false;
+ for p in predicates {
+ match p.kind().skip_binder() {
+ ClauseKind::Trait(trait_ref) => {
+ let trait_ = trait_ref.def_id().0;
+ if default_sized.is_sized_trait(trait_, f.interner) {
+ is_sized = true;
+ if matches!(default_sized, SizedByDefault::Sized { .. }) {
+ // Don't print +Sized, but rather +?Sized if absent.
+ continue;
+ }
+ }
+
+ distinct_bounds += 1;
+ }
+ ClauseKind::TypeOutlives(to) if Either::Left(to.0) == this => distinct_bounds += 1,
+ ClauseKind::RegionOutlives(lo) if Either::Right(lo.0) == this => distinct_bounds += 1,
+ _ => {}
+ }
}
+
+ if let SizedByDefault::Sized { .. } = default_sized
+ && !is_sized
+ {
+ distinct_bounds += 1;
+ }
+
+ distinct_bounds > 1
}
fn write_bounds_like_dyn_trait<'db>(
@@ -1855,7 +1846,7 @@ fn write_bounds_like_dyn_trait<'db>(
match p.kind().skip_binder() {
ClauseKind::Trait(trait_ref) => {
let trait_ = trait_ref.def_id().0;
- if default_sized.is_sized_trait(trait_, f.db) {
+ if default_sized.is_sized_trait(trait_, f.interner) {
is_sized = true;
if matches!(default_sized, SizedByDefault::Sized { .. }) {
// Don't print +Sized, but rather +?Sized if absent.
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 36630ab587..438699b409 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -608,7 +608,7 @@ trait Foo {}
fn test(f: impl Foo, g: &(impl Foo + ?Sized)) {
let _: &dyn Foo = &f;
let _: &dyn Foo = g;
- //^ expected &'? (dyn Foo + 'static), got &'? impl Foo + ?Sized
+ //^ expected &'? (dyn Foo + 'static), got &'? (impl Foo + ?Sized)
}
"#,
);
diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs
index dc3869930d..37da7fc875 100644
--- a/crates/hir-ty/src/tests/display_source_code.rs
+++ b/crates/hir-ty/src/tests/display_source_code.rs
@@ -111,7 +111,7 @@ fn test(
b;
//^ impl Foo
c;
- //^ &impl Foo + ?Sized
+ //^ &(impl Foo + ?Sized)
d;
//^ S<impl Foo>
ref_any;
@@ -192,7 +192,7 @@ fn test(
b;
//^ fn(impl Foo) -> impl Foo
c;
-} //^ fn(&impl Foo + ?Sized) -> &impl Foo + ?Sized
+} //^ fn(&(impl Foo + ?Sized)) -> &(impl Foo + ?Sized)
"#,
);
}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 390553c0d7..1a0bd44798 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -4819,7 +4819,7 @@ fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
431..433 '{}': ()
447..450 'baz': impl Baz<Assoc = impl Foo>
480..482 '{}': ()
- 500..503 'baz': impl Baz<Assoc = &'a impl Foo + 'a>
+ 500..503 'baz': impl Baz<Assoc = &'a (impl Foo + 'a)>
544..546 '{}': ()
560..563 'baz': impl Baz<Assoc = Qux<impl Foo>>
598..600 '{}': ()
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 1f9af564c3..b4440dfa18 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -587,6 +587,7 @@ impl<'db> HirDisplay<'db> for TypeParam {
Either::Left(ty),
&predicates,
SizedByDefault::Sized { anchor: krate },
+ false,
);
}
},
@@ -614,6 +615,7 @@ impl<'db> HirDisplay<'db> for TypeParam {
Either::Left(ty),
&predicates,
default_sized,
+ false,
)?;
}
Ok(())
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index c74e3104c1..caf7cc714d 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -1382,4 +1382,21 @@ fn f<'a>() {
"#]],
);
}
+
+ #[test]
+ fn ref_multi_trait_impl_trait() {
+ check_with_config(
+ InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: sized
+trait Eq {}
+trait Ord {}
+
+fn foo(argument: &(impl Eq + Ord)) {
+ let x = argument;
+ // ^ &(impl Eq + Ord)
+}
+ "#,
+ );
+ }
}