diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index 0abd14eebf..9f33fd26e8 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs @@ -307,7 +307,9 @@ impl TypeBound { Some(_) => TraitBoundModifier::Maybe, None => TraitBoundModifier::None, }; - lower_path_type(path_type).map(|p| TypeBound::Path(p, m)).unwrap_or(TypeBound::Error) + lower_path_type(path_type) + .map(|p| TypeBound::Path(p, m)) + .unwrap_or(TypeBound::Error) } ast::TypeBoundKind::ForType(for_type) => { let lt_refs = match for_type.generic_param_list() { diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index b47c0a5de3..2472d9ce20 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -377,10 +377,20 @@ impl HirDisplay for Ty { } // FIXME: all this just to decide whether to use parentheses... - let datas; - let predicates: Vec<_> = match t.kind(&Interner) { + let conains_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(), conains_impl_fn(bounds)) } TyKind::Alias(AliasTy::Opaque(OpaqueTy { opaque_ty_id, @@ -390,33 +400,54 @@ impl HirDisplay for Ty { 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 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, conains_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, ")")?; @@ -1085,7 +1116,6 @@ impl HirDisplay for TypeBound { fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { match self { TypeBound::Path(path, modifier) => { - // todo don't print implicit Sized; implicit ?Sized on Self of a trait match modifier { TraitBoundModifier::None => (), TraitBoundModifier::Maybe => write!(f, "?")?, diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 2bcd0fcb5d..b72e1fc46a 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -828,7 +828,9 @@ impl<'a> TyLoweringContext<'a> { trait_ref: TraitRef, ) -> impl Iterator + 'a { let last_segment = match bound { - TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => path.segments().last(), + TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => { + path.segments().last() + } TypeBound::Path(_, TraitBoundModifier::Maybe) | TypeBound::Error | TypeBound::Lifetime(_) => None, diff --git a/crates/hir_ty/src/tests/display_source_code.rs b/crates/hir_ty/src/tests/display_source_code.rs index 971ad495fa..240942e488 100644 --- a/crates/hir_ty/src/tests/display_source_code.rs +++ b/crates/hir_ty/src/tests/display_source_code.rs @@ -42,16 +42,15 @@ fn main() { #[test] fn render_raw_ptr_impl_ty() { - // FIXME: remove parens, they apper because there is an implicit Sized bound check_types_source_code( r#" //- minicore: sized trait Unpin {} -fn foo() -> *const impl Unpin { loop {} } +fn foo() -> *const (impl Unpin + Sized) { loop {} } fn main() { let foo = foo(); foo; -} //^^^ *const (impl Unpin) +} //^^^ *const impl Unpin "#, ); } @@ -94,9 +93,9 @@ fn test( d; //^ S ref_any; - //^ &impl ?Sized + //^^^^^^^ &impl ?Sized empty; -} //^ impl Sized +} //^^^^^ impl Sized "#, ); } @@ -107,11 +106,50 @@ fn sized_bounds_rpit() { r#" //- minicore: sized trait Foo {} -fn foo() -> impl Foo { loop {} } -fn test() { - let foo = foo(); +fn foo1() -> impl Foo { loop {} } +fn foo2() -> impl Foo + Sized { loop {} } +fn foo3() -> impl Foo + ?Sized { loop {} } +fn test() { + let foo = foo1(); foo; -} //^ impl Foo + //^^^ impl Foo + let foo = foo2(); + foo; + //^^^ impl Foo + let foo = foo3(); + foo; +} //^^^ impl Foo + ?Sized +"#, + ); +} + +#[test] +fn parenthesize_ptr_rpit_sized_bounds() { + check_types_source_code( + r#" +//- minicore: sized +trait Foo {} +fn foo1() -> *const impl Foo { loop {} } +fn foo2() -> *const (impl Foo + Sized) { loop {} } +fn foo3() -> *const (impl Sized + Foo) { loop {} } +fn foo4() -> *const (impl Foo + ?Sized) { loop {} } +fn foo5() -> *const (impl ?Sized + Foo) { loop {} } +fn test() { + let foo = foo1(); + foo; + //^^^ *const impl Foo + let foo = foo2(); + foo; + //^^^ *const impl Foo + let foo = foo3(); + foo; + //^^^ *const impl Foo + let foo = foo4(); + foo; + //^^^ *const (impl Foo + ?Sized) + let foo = foo5(); + foo; +} //^^^ *const (impl Foo + ?Sized) "#, ); } diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 1e40ff24e3..ed05d55f91 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -944,7 +944,7 @@ fn lifetime_from_chalk_during_deref() { r#" //- minicore: deref struct Box {} -impl core::ops::Deref for Box { +impl core::ops::Deref for Box { type Target = T; fn deref(&self) -> &Self::Target { @@ -1063,6 +1063,7 @@ fn cfg_tail() { fn impl_trait_in_option_9530() { check_types( r#" +//- minicore: sized struct Option; impl Option { fn unwrap(self) -> T { loop {} } diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 79ff84c633..35854e42c1 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs @@ -3534,9 +3534,7 @@ fn test() { fn associated_type_sized_bounds() { check_infer( r#" -#[lang = "sized"] -pub trait Sized {} - +//- minicore: sized struct Yes; trait IsSized { const IS_SIZED: Yes; } impl IsSized for T { const IS_SIZED: Yes = Yes; } @@ -3553,11 +3551,11 @@ fn f() { } "#, expect![[r#" - 142..145 'Yes': Yes - 250..333 '{ ...ZED; }': () - 256..277 'F::Exp..._SIZED': Yes - 283..304 'F::Imp..._SIZED': Yes - 310..330 'F::Rel..._SIZED': {unknown} + 104..107 'Yes': Yes + 212..295 '{ ...ZED; }': () + 218..239 'F::Exp..._SIZED': Yes + 245..266 'F::Imp..._SIZED': Yes + 272..292 'F::Rel..._SIZED': {unknown} "#]], ); } diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 951af50186..e3d63cc949 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -932,6 +932,50 @@ fn main() { ) } + #[test] + fn fn_hints_ptr_rpit_fn_parentheses() { + check_types( + r#" +//- minicore: fn, sized +trait Trait {} + +fn foo1() -> *const impl Fn() { loop {} } +fn foo2() -> *const (impl Fn() + Sized) { loop {} } +fn foo3() -> *const (impl Fn() + ?Sized) { loop {} } +fn foo4() -> *const (impl Sized + Fn()) { loop {} } +fn foo5() -> *const (impl ?Sized + Fn()) { loop {} } +fn foo6() -> *const (impl Fn() + Trait) { loop {} } +fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} } +fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} } +fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} } +fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} } + +fn main() { + let foo = foo1(); + // ^^^ *const impl Fn() + let foo = foo2(); + // ^^^ *const impl Fn() + let foo = foo3(); + // ^^^ *const (impl Fn() + ?Sized) + let foo = foo4(); + // ^^^ *const impl Fn() + let foo = foo5(); + // ^^^ *const (impl Fn() + ?Sized) + let foo = foo6(); + // ^^^ *const (impl Fn() + Trait) + let foo = foo7(); + // ^^^ *const (impl Fn() + Trait) + let foo = foo8(); + // ^^^ *const (impl Fn() + Trait + ?Sized) + let foo = foo9(); + // ^^^ *const (impl Fn() -> u8 + ?Sized) + let foo = foo10(); + // ^^^ *const impl Fn() +} +"#, + ) + } + #[test] fn unit_structs_have_no_type_hints() { check_types(