diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index e9e949c47f..d2f1b40148 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -1,14 +1,15 @@ //! FIXME: write short doc here -use std::fmt; +use std::{borrow::Cow, fmt}; use crate::{ db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate, - Lifetime, Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, + Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, }; +use arrayvec::ArrayVec; use hir_def::{ - find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, - Lookup, ModuleId, + db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, + AssocContainerId, HasModule, Lookup, ModuleId, TraitId, }; use hir_expand::name::Name; @@ -257,25 +258,45 @@ impl HirDisplay for ApplicationTy { t.hir_fmt(f)?; write!(f, "; _]")?; } - TypeCtor::RawPtr(m) => { - let t = self.parameters.as_single(); - - write!(f, "*{}", m.as_keyword_for_ptr())?; - if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { - write!(f, "(")?; - t.hir_fmt(f)?; - write!(f, ")")?; - } else { - t.hir_fmt(f)?; - } - } - TypeCtor::Ref(m) => { + TypeCtor::RawPtr(m) | TypeCtor::Ref(m) => { let t = self.parameters.as_single(); let ty_display = t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target); - write!(f, "&{}", m.as_keyword_for_ref())?; - if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { + if matches!(self.ctor, TypeCtor::RawPtr(_)) { + write!(f, "*{}", m.as_keyword_for_ptr())?; + } else { + write!(f, "&{}", m.as_keyword_for_ref())?; + } + + let datas; + let predicates = match t { + Ty::Dyn(predicates) if predicates.len() > 1 => { + Cow::Borrowed(predicates.as_ref()) + } + &Ty::Opaque(OpaqueTy { + opaque_ty_id: OpaqueTyId::ReturnTypeImplTrait(func, idx), + ref parameters, + }) => { + 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.subst(parameters); + Cow::Owned(bounds.value) + } + _ => Cow::Borrowed(&[][..]), + }; + + if let [GenericPredicate::Implemented(trait_ref), _] = predicates.as_ref() { + let trait_ = trait_ref.trait_; + if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) { + return write!(f, "{}", ty_display); + } + } + + if predicates.len() > 1 { write!(f, "(")?; write!(f, "{}", ty_display)?; write!(f, ")")?; @@ -595,6 +616,17 @@ impl HirDisplay for FnSig { } } +fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator { + let krate = trait_.lookup(db).container.module(db).krate; + let fn_traits = [ + db.lang_item(krate, "fn".into()), + db.lang_item(krate, "fn_mut".into()), + db.lang_item(krate, "fn_once".into()), + ]; + // FIXME: Replace ArrayVec when into_iter is a thing on arrays + ArrayVec::from(fn_traits).into_iter().flatten().flat_map(|it| it.as_trait()) +} + pub fn write_bounds_like_dyn_trait( predicates: &[GenericPredicate], f: &mut HirFormatter, @@ -607,10 +639,15 @@ pub fn write_bounds_like_dyn_trait( // predicate for that trait). let mut first = true; let mut angle_open = false; + let mut is_fn_trait = false; for p in predicates.iter() { match p { GenericPredicate::Implemented(trait_ref) => { - if angle_open { + let trait_ = trait_ref.trait_; + if !is_fn_trait { + is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_); + } + if !is_fn_trait && angle_open { write!(f, ">")?; angle_open = false; } @@ -620,14 +657,27 @@ pub fn write_bounds_like_dyn_trait( // We assume that the self type is $0 (i.e. the // 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_ref.trait_).name)?; - if trait_ref.substs.len() > 1 { - write!(f, "<")?; - f.write_joined(&trait_ref.substs[1..], ", ")?; - // there might be assoc type bindings, so we leave the angle brackets open - angle_open = true; + write!(f, "{}", f.db.trait_data(trait_).name)?; + if let [_, params @ ..] = &*trait_ref.substs.0 { + if is_fn_trait { + if let Some(args) = params.first().and_then(|it| it.as_tuple()) { + write!(f, "(")?; + f.write_joined(&*args.0, ", ")?; + write!(f, ")")?; + } + } else if !params.is_empty() { + write!(f, "<")?; + f.write_joined(params, ", ")?; + // there might be assoc type bindings, so we leave the angle brackets open + angle_open = true; + } } } + GenericPredicate::Projection(projection_pred) if is_fn_trait => { + is_fn_trait = false; + write!(f, " -> ")?; + projection_pred.ty.hir_fmt(f)?; + } GenericPredicate::Projection(projection_pred) => { // in types in actual Rust, these will always come // after the corresponding Implemented predicate diff --git a/crates/hir_ty/src/tests/display_source_code.rs b/crates/hir_ty/src/tests/display_source_code.rs index b502135d8e..3d29021aaf 100644 --- a/crates/hir_ty/src/tests/display_source_code.rs +++ b/crates/hir_ty/src/tests/display_source_code.rs @@ -39,3 +39,18 @@ fn main() { "#, ); } + +#[test] +fn render_raw_ptr_impl_ty() { + check_types_source_code( + r#" +trait Sized {} +trait Unpin {} +fn foo() -> *const (impl Unpin + Sized) { loop {} } +fn main() { + let foo = foo(); + foo; +} //^ *const (impl Unpin + Sized) +"#, + ); +} diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 41d0975197..e5a3f95a6c 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs @@ -3038,16 +3038,16 @@ fn infer_box_fn_arg() { 406..417 '&self.inner': &*mut T 407..411 'self': &Box 407..417 'self.inner': *mut T - 478..575 '{ ...(&s) }': FnOnce::Output,)>, (&Option,)> + 478..575 '{ ...(&s) }': FnOnce::Output), (&Option,)> 488..489 's': Option 492..504 'Option::None': Option - 514..515 'f': Box,)>> + 514..515 'f': Box)> 549..562 'box (|ps| {})': Box<|{unknown}| -> ()> 554..561 '|ps| {}': |{unknown}| -> () 555..557 'ps': {unknown} 559..561 '{}': () - 568..569 'f': Box,)>> - 568..573 'f(&s)': FnOnce::Output,)>, (&Option,)> + 568..569 'f': Box)> + 568..573 'f(&s)': FnOnce::Output), (&Option,)> 570..572 '&s': &Option 571..572 's': Option "#]], diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 3e9a65d9c7..a2039fcc73 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1398,6 +1398,43 @@ fn main() { Foo::bar(&Foo); //^^^^ self } +"#, + ) + } + + #[test] + fn fn_hints() { + check( + r#" +trait Sized {} + +fn foo() -> impl Fn() { loop {} } +fn foo1() -> impl Fn(f64) { loop {} } +fn foo2() -> impl Fn(f64, f64) { loop {} } +fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} } +fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} } +fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} } +fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} } +fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} } + +fn main() { + let foo = foo(); + // ^^^ impl Fn() + let foo = foo1(); + // ^^^ impl Fn(f64) + let foo = foo2(); + // ^^^ impl Fn(f64, f64) + let foo = foo3(); + // ^^^ impl Fn(f64, f64) -> u32 + let foo = foo4(); + // ^^^ &dyn Fn(f64, f64) -> u32 + let foo = foo5(); + // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 + let foo = foo6(); + // ^^^ impl Fn(f64, f64) -> u32 + Sized + let foo = foo7(); + // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized) +} "#, ) } diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs index f3d355861e..5e88de64db 100644 --- a/crates/ide_db/src/helpers/famous_defs_fixture.rs +++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs @@ -1,4 +1,5 @@ //- /libcore.rs crate:core +//! Signatures of traits, types and functions from the core lib for use in tests. pub mod convert { pub trait From { fn from(t: T) -> Self; diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index a957e36afe..6abad189ad 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -324,7 +324,7 @@ impl TidyDocs { } fn is_exclude_file(d: &Path) -> bool { - let file_names = ["tests.rs"]; + let file_names = ["tests.rs", "famous_defs_fixture.rs"]; d.file_name() .unwrap_or_default()