mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +00:00
Auto merge of #12982 - jridgewell:into_future, r=Veykril
Implement IntoFuture type inference One of my projects is using [IntoFuture](https://doc.rust-lang.org/std/future/trait.IntoFuture.html) to make our async code a little less verbose. However, rust-analyzer can't infer the output type of an await expression if the value uses `IntoFuture` to convert into another type. So we're getting `{unknown}` types everywhere since switching. `foo.await` itself [desugars](e4417cf020/compiler/rustc_ast_lowering/src/expr.rs (L644-L658)
) into a `match into_future(foo) {}`, with every `Future` impl getting a [default](e4417cf020/library/core/src/future/into_future.rs (L131-L139)
) `IntoFuture` implementation. I'm not sure if we want to disable the old `future_trait` paths, since this only recently [stabilize](https://github.com/rust-lang/rust/pull/98718).
This commit is contained in:
commit
1da9156b0d
11 changed files with 178 additions and 35 deletions
|
@ -257,6 +257,7 @@ macro_rules! __known_path {
|
||||||
(core::ops::RangeToInclusive) => {};
|
(core::ops::RangeToInclusive) => {};
|
||||||
(core::ops::RangeInclusive) => {};
|
(core::ops::RangeInclusive) => {};
|
||||||
(core::future::Future) => {};
|
(core::future::Future) => {};
|
||||||
|
(core::future::IntoFuture) => {};
|
||||||
(core::ops::Try) => {};
|
(core::ops::Try) => {};
|
||||||
($path:path) => {
|
($path:path) => {
|
||||||
compile_error!("Please register your known path in the path module")
|
compile_error!("Please register your known path in the path module")
|
||||||
|
|
|
@ -266,6 +266,7 @@ pub mod known {
|
||||||
Try,
|
Try,
|
||||||
Ok,
|
Ok,
|
||||||
Future,
|
Future,
|
||||||
|
IntoFuture,
|
||||||
Result,
|
Result,
|
||||||
Option,
|
Option,
|
||||||
Output,
|
Output,
|
||||||
|
@ -399,6 +400,7 @@ pub mod known {
|
||||||
future_trait,
|
future_trait,
|
||||||
index,
|
index,
|
||||||
index_mut,
|
index_mut,
|
||||||
|
into_future,
|
||||||
mul_assign,
|
mul_assign,
|
||||||
mul,
|
mul,
|
||||||
neg,
|
neg,
|
||||||
|
|
|
@ -875,7 +875,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
|
fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
|
||||||
let trait_ = self.resolve_lang_item(name![future_trait])?.as_trait()?;
|
let trait_ = self
|
||||||
|
.resolver
|
||||||
|
.resolve_known_trait(self.db.upcast(), &path![core::future::IntoFuture])
|
||||||
|
.or_else(|| self.resolve_lang_item(name![future_trait])?.as_trait())?;
|
||||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,31 @@ fn not_send() -> Box<dyn Future<Output = ()> + 'static> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn into_future_trait() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
//- minicore: future
|
||||||
|
struct Futurable;
|
||||||
|
impl core::future::IntoFuture for Futurable {
|
||||||
|
type Output = u64;
|
||||||
|
type IntoFuture = IntFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IntFuture;
|
||||||
|
impl core::future::Future for IntFuture {
|
||||||
|
type Output = u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let r = Futurable;
|
||||||
|
let v = r.await;
|
||||||
|
v;
|
||||||
|
} //^ u64
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_try() {
|
fn infer_try() {
|
||||||
check_types(
|
check_types(
|
||||||
|
|
|
@ -2778,20 +2778,32 @@ impl Type {
|
||||||
self.ty.is_unknown()
|
self.ty.is_unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that particular type `ty` implements `std::future::Future`.
|
/// Checks that particular type `ty` implements `std::future::IntoFuture` or
|
||||||
|
/// `std::future::Future`.
|
||||||
/// This function is used in `.await` syntax completion.
|
/// This function is used in `.await` syntax completion.
|
||||||
pub fn impls_future(&self, db: &dyn HirDatabase) -> bool {
|
pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool {
|
||||||
let std_future_trait = db
|
let trait_ = db
|
||||||
.lang_item(self.env.krate, SmolStr::new_inline("future_trait"))
|
.lang_item(self.env.krate, SmolStr::new_inline("into_future"))
|
||||||
.and_then(|it| it.as_trait());
|
.and_then(|it| {
|
||||||
let std_future_trait = match std_future_trait {
|
let into_future_fn = it.as_function()?;
|
||||||
|
let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?;
|
||||||
|
let into_future_trait = assoc_item.containing_trait_or_trait_impl(db)?;
|
||||||
|
Some(into_future_trait.id)
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
let future_trait =
|
||||||
|
db.lang_item(self.env.krate, SmolStr::new_inline("future_trait"))?;
|
||||||
|
future_trait.as_trait()
|
||||||
|
});
|
||||||
|
|
||||||
|
let trait_ = match trait_ {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let canonical_ty =
|
let canonical_ty =
|
||||||
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
||||||
method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), std_future_trait)
|
method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), trait_)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that particular type `ty` implements `std::ops::FnOnce`.
|
/// Checks that particular type `ty` implements `std::ops::FnOnce`.
|
||||||
|
|
|
@ -27,6 +27,7 @@ use hir_def::{
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
builtin_fn_macro::BuiltinFnLikeExpander,
|
builtin_fn_macro::BuiltinFnLikeExpander,
|
||||||
hygiene::Hygiene,
|
hygiene::Hygiene,
|
||||||
|
mod_path::path,
|
||||||
name,
|
name,
|
||||||
name::{AsName, Name},
|
name::{AsName, Name},
|
||||||
HirFileId, InFile,
|
HirFileId, InFile,
|
||||||
|
@ -269,14 +270,35 @@ impl SourceAnalyzer {
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
await_expr: &ast::AwaitExpr,
|
await_expr: &ast::AwaitExpr,
|
||||||
) -> Option<FunctionId> {
|
) -> Option<FunctionId> {
|
||||||
let ty = self.ty_of_expr(db, &await_expr.expr()?.into())?;
|
let mut ty = self.ty_of_expr(db, &await_expr.expr()?.into())?.clone();
|
||||||
|
|
||||||
let op_fn = db
|
let into_future_trait = self
|
||||||
|
.resolver
|
||||||
|
.resolve_known_trait(db.upcast(), &path![core::future::IntoFuture])
|
||||||
|
.map(Trait::from);
|
||||||
|
|
||||||
|
if let Some(into_future_trait) = into_future_trait {
|
||||||
|
let type_ = Type::new_with_resolver(db, &self.resolver, ty.clone());
|
||||||
|
if type_.impls_trait(db, into_future_trait, &[]) {
|
||||||
|
let items = into_future_trait.items(db);
|
||||||
|
let into_future_type = items.into_iter().find_map(|item| match item {
|
||||||
|
AssocItem::TypeAlias(alias)
|
||||||
|
if alias.name(db) == hir_expand::name![IntoFuture] =>
|
||||||
|
{
|
||||||
|
Some(alias)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})?;
|
||||||
|
let future_trait = type_.normalize_trait_assoc_type(db, &[], into_future_type)?;
|
||||||
|
ty = future_trait.ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let poll_fn = db
|
||||||
.lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())?
|
.lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())?
|
||||||
.as_function()?;
|
.as_function()?;
|
||||||
let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
|
let substs = hir_ty::TyBuilder::subst_for_def(db, poll_fn).push(ty.clone()).build();
|
||||||
|
Some(self.resolve_impl_method_or_trait_def(db, poll_fn, &substs))
|
||||||
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_prefix_expr(
|
pub(crate) fn resolve_prefix_expr(
|
||||||
|
|
|
@ -55,6 +55,7 @@ const USELESS_METHODS: &[&str] = &[
|
||||||
"iter",
|
"iter",
|
||||||
"into_iter",
|
"into_iter",
|
||||||
"iter_mut",
|
"iter_mut",
|
||||||
|
"into_future",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr {
|
pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr {
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub(crate) fn complete_dot(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Suggest .await syntax for types that implement Future trait
|
// Suggest .await syntax for types that implement Future trait
|
||||||
if receiver_ty.impls_future(ctx.db) {
|
if receiver_ty.impls_into_future(ctx.db) {
|
||||||
let mut item =
|
let mut item =
|
||||||
CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await");
|
CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await");
|
||||||
item.detail("expr.await");
|
item.detail("expr.await");
|
||||||
|
|
|
@ -75,16 +75,17 @@ impl Future for A {}
|
||||||
fn foo(a: A) { a.$0 }
|
fn foo(a: A) { a.$0 }
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
kw await expr.await
|
kw await expr.await
|
||||||
sn box Box::new(expr)
|
me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
|
||||||
sn call function(expr)
|
sn box Box::new(expr)
|
||||||
sn dbg dbg!(expr)
|
sn call function(expr)
|
||||||
sn dbgr dbg!(&expr)
|
sn dbg dbg!(expr)
|
||||||
sn let let
|
sn dbgr dbg!(&expr)
|
||||||
sn letm let mut
|
sn let let
|
||||||
sn match match expr {}
|
sn letm let mut
|
||||||
sn ref &expr
|
sn match match expr {}
|
||||||
sn refm &mut expr
|
sn ref &expr
|
||||||
|
sn refm &mut expr
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -98,18 +99,45 @@ fn foo() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
kw await expr.await
|
kw await expr.await
|
||||||
sn box Box::new(expr)
|
me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
|
||||||
sn call function(expr)
|
sn box Box::new(expr)
|
||||||
sn dbg dbg!(expr)
|
sn call function(expr)
|
||||||
sn dbgr dbg!(&expr)
|
sn dbg dbg!(expr)
|
||||||
sn let let
|
sn dbgr dbg!(&expr)
|
||||||
sn letm let mut
|
sn let let
|
||||||
sn match match expr {}
|
sn letm let mut
|
||||||
sn ref &expr
|
sn match match expr {}
|
||||||
sn refm &mut expr
|
sn ref &expr
|
||||||
|
sn refm &mut expr
|
||||||
"#]],
|
"#]],
|
||||||
)
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completion_await_impls_into_future() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: future
|
||||||
|
use core::future::*;
|
||||||
|
struct A {}
|
||||||
|
impl IntoFuture for A {}
|
||||||
|
fn foo(a: A) { a.$0 }
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
kw await expr.await
|
||||||
|
me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
|
||||||
|
sn box Box::new(expr)
|
||||||
|
sn call function(expr)
|
||||||
|
sn dbg dbg!(expr)
|
||||||
|
sn dbgr dbg!(&expr)
|
||||||
|
sn let let
|
||||||
|
sn letm let mut
|
||||||
|
sn match match expr {}
|
||||||
|
sn ref &expr
|
||||||
|
sn refm &mut expr
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1664,6 +1664,40 @@ fn f() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_await_into_future_poll() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: future
|
||||||
|
|
||||||
|
struct Futurable;
|
||||||
|
|
||||||
|
impl core::future::IntoFuture for Futurable {
|
||||||
|
type IntoFuture = MyFut;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyFut;
|
||||||
|
|
||||||
|
impl core::future::Future for MyFut {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
//^^^^
|
||||||
|
self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>
|
||||||
|
) -> std::task::Poll<Self::Output>
|
||||||
|
{
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
Futurable.await$0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_try_op() {
|
fn goto_try_op() {
|
||||||
check(
|
check(
|
||||||
|
|
|
@ -471,6 +471,21 @@ pub mod future {
|
||||||
#[lang = "poll"]
|
#[lang = "poll"]
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait IntoFuture {
|
||||||
|
type Output;
|
||||||
|
type IntoFuture: Future<Output = Self::Output>;
|
||||||
|
#[lang = "into_future"]
|
||||||
|
fn into_future(self) -> Self::IntoFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future> IntoFuture for F {
|
||||||
|
type Output = F::Output;
|
||||||
|
type IntoFuture = F;
|
||||||
|
fn into_future(self) -> F {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub mod task {
|
pub mod task {
|
||||||
pub enum Poll<T> {
|
pub enum Poll<T> {
|
||||||
|
|
Loading…
Reference in a new issue