From 251ef93ac3bbb138a2eedf6090f2f56f1a15d898 Mon Sep 17 00:00:00 2001 From: oxalica Date: Thu, 10 Sep 2020 20:01:23 +0800 Subject: Implement async blocks --- crates/hir/src/code_model.rs | 2 + crates/hir_def/src/body/lower.rs | 5 +- crates/hir_def/src/expr.rs | 5 +- crates/hir_ty/src/display.rs | 28 ++++--- crates/hir_ty/src/infer/expr.rs | 11 ++- crates/hir_ty/src/lib.rs | 39 +++++++++- crates/hir_ty/src/tests/simple.rs | 45 ++++++----- crates/hir_ty/src/tests/traits.rs | 40 ++++++++++ crates/hir_ty/src/traits/chalk.rs | 103 ++++++++++++++++++++------ crates/hir_ty/src/traits/chalk/tls.rs | 3 + crates/ide/src/completion/complete_keyword.rs | 22 ++++++ 11 files changed, 248 insertions(+), 55 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index c2fc819e7..613b35afd 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs @@ -1283,6 +1283,8 @@ impl Type { /// Checks that particular type `ty` implements `std::future::Future`. /// This function is used in `.await` syntax completion. pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { + // No special case for the type of async block, since Chalk can figure it out. + let krate = self.krate; let std_future_trait = diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 30ac12a12..c5ebc2aa0 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -239,7 +239,10 @@ impl ExprCollector<'_> { None => self.missing_expr(), }, // FIXME: we need to record these effects somewhere... - ast::Effect::Async(_) => self.collect_block_opt(e.block_expr()), + ast::Effect::Async(_) => { + let body = self.collect_block_opt(e.block_expr()); + self.alloc_expr(Expr::Async { body }, syntax_ptr) + } }, ast::Expr::BlockExpr(e) => self.collect_block(e), ast::Expr::LoopExpr(e) => { diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index c94b3a36f..e862c2080 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs @@ -111,6 +111,9 @@ pub enum Expr { TryBlock { body: ExprId, }, + Async { + body: ExprId, + }, Cast { expr: ExprId, type_ref: TypeRef, @@ -250,7 +253,7 @@ impl Expr { f(*expr); } } - Expr::TryBlock { body } | Expr::Unsafe { body } => f(*body), + Expr::TryBlock { body } | Expr::Unsafe { body } | Expr::Async { body } => f(*body), Expr::Loop { body, .. } => f(*body), Expr::While { condition, body, .. } => { f(*condition); diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 64b68014d..efb48c7ee 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -381,19 +381,24 @@ impl HirDisplay for ApplicationTy { } } TypeCtor::OpaqueType(opaque_ty_id) => { - let bounds = match opaque_ty_id { + match opaque_ty_id { OpaqueTyId::ReturnTypeImplTrait(func, idx) => { 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()); - data.subst(&self.parameters) + let bounds = data.subst(&self.parameters); + write!(f, "impl ")?; + write_bounds_like_dyn_trait(&bounds.value, f)?; + // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } - }; - write!(f, "impl ")?; - write_bounds_like_dyn_trait(&bounds.value, f)?; - // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution + OpaqueTyId::AsyncBlockTypeImplTrait(..) => { + write!(f, "impl Future")?; + } + } } TypeCtor::Closure { .. } => { let sig = self.parameters[0].callable_sig(f.db); @@ -474,18 +479,21 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait(predicates, f)?; } Ty::Opaque(opaque_ty) => { - let bounds = match opaque_ty.opaque_ty_id { + match opaque_ty.opaque_ty_id { OpaqueTyId::ReturnTypeImplTrait(func, idx) => { 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()); - data.subst(&opaque_ty.parameters) + let bounds = data.subst(&opaque_ty.parameters); + write!(f, "impl ")?; + write_bounds_like_dyn_trait(&bounds.value, f)?; + } + OpaqueTyId::AsyncBlockTypeImplTrait(..) => { + write!(f, "{{async block}}")?; } }; - write!(f, "impl ")?; - write_bounds_like_dyn_trait(&bounds.value, f)?; } Ty::Unknown => write!(f, "{{unknown}}")?, Ty::Infer(..) => write!(f, "_")?, diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index a2f849d02..0a141b9cb 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -17,8 +17,8 @@ use crate::{ autoderef, method_resolution, op, traits::{FnTrait, InEnvironment}, utils::{generics, variant_data, Generics}, - ApplicationTy, Binders, CallableDefId, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, - TraitRef, Ty, TypeCtor, + ApplicationTy, Binders, CallableDefId, InferTy, IntTy, Mutability, Obligation, OpaqueTyId, + Rawness, Substs, TraitRef, Ty, TypeCtor, }; use super::{ @@ -146,6 +146,13 @@ impl<'a> InferenceContext<'a> { // FIXME should be std::result::Result<{inner}, _> Ty::Unknown } + Expr::Async { body } => { + // Use the first type parameter as the output type of future. + // existenail type AsyncBlockImplTrait: Future + let inner_ty = self.infer_expr(*body, &Expectation::none()); + let opaque_ty_id = OpaqueTyId::AsyncBlockTypeImplTrait(self.owner, *body); + Ty::apply_one(TypeCtor::OpaqueType(opaque_ty_id), inner_ty) + } Expr::Loop { body, label } => { self.breakables.push(BreakableContext { may_break: false, diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 1e748476a..4a7d3a0d9 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -33,6 +33,7 @@ use hir_def::{ AdtId, AssocContainerId, DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, }; +use hir_expand::name::name; use itertools::Itertools; use crate::{ @@ -129,8 +130,9 @@ pub enum TypeCtor { /// This represents a placeholder for an opaque type in situations where we /// don't know the hidden type (i.e. currently almost always). This is - /// analogous to the `AssociatedType` type constructor. As with that one, - /// these are only produced by Chalk. + /// analogous to the `AssociatedType` type constructor. + /// It is also used as the type of async block, with one type parameter + /// representing the Future::Output type. OpaqueType(OpaqueTyId), /// The type of a specific closure. @@ -173,6 +175,8 @@ impl TypeCtor { let generic_params = generics(db.upcast(), func.into()); generic_params.len() } + // 1 param representing Future::Output type. + OpaqueTyId::AsyncBlockTypeImplTrait(..) => 1, } } TypeCtor::FnPtr { num_args, is_varargs: _ } => num_args as usize + 1, @@ -205,6 +209,7 @@ impl TypeCtor { OpaqueTyId::ReturnTypeImplTrait(func, _) => { Some(func.lookup(db.upcast()).module(db.upcast()).krate) } + OpaqueTyId::AsyncBlockTypeImplTrait(def, _) => Some(def.module(db.upcast()).krate), }, } } @@ -843,6 +848,33 @@ impl Ty { pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option> { match self { + Ty::Apply(ApplicationTy { ctor: TypeCtor::OpaqueType(opaque_ty_id), parameters }) => { + match opaque_ty_id { + OpaqueTyId::AsyncBlockTypeImplTrait(def, _expr) => { + let krate = def.module(db.upcast()).krate; + if let Some(future_output) = db + .lang_item(krate, "future_trait".into()) + .and_then(|item| item.as_trait()) + .and_then(|trait_| { + db.trait_data(trait_).associated_type_by_name(&name![Output]) + }) + { + let proj = GenericPredicate::Projection(ProjectionPredicate { + projection_ty: ProjectionTy { + associated_ty: future_output, + // Self type. + parameters: Substs::single(self.clone()), + }, + ty: parameters[0].clone(), + }); + Some(vec![proj]) + } else { + None + } + } + OpaqueTyId::ReturnTypeImplTrait(..) => None, + } + } Ty::Opaque(opaque_ty) => { let predicates = match opaque_ty.opaque_ty_id { OpaqueTyId::ReturnTypeImplTrait(func, idx) => { @@ -853,6 +885,8 @@ impl Ty { data.subst(&opaque_ty.parameters) }) } + // It always has an parameter for Future::Output type. + OpaqueTyId::AsyncBlockTypeImplTrait(..) => unreachable!(), }; predicates.map(|it| it.value) @@ -1065,6 +1099,7 @@ impl TypeWalk for Vec { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum OpaqueTyId { ReturnTypeImplTrait(hir_def::FunctionId, u16), + AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } #[derive(Clone, PartialEq, Eq, Debug, Hash)] diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 48db23a34..5b07948f3 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -1889,31 +1889,40 @@ fn fn_pointer_return() { fn effects_smoke_test() { check_infer( r#" - fn main() { + async fn main() { let x = unsafe { 92 }; let y = async { async { () }.await }; let z = try { () }; let t = 'a: { 92 }; } + + #[prelude_import] use future::*; + + mod future { + #[lang = "future_trait"] + pub trait Future { type Output; } + } "#, expect![[r#" - 10..130 '{ ...2 }; }': () - 20..21 'x': i32 - 24..37 'unsafe { 92 }': i32 - 31..37 '{ 92 }': i32 - 33..35 '92': i32 - 47..48 'y': {unknown} - 57..79 '{ asyn...wait }': {unknown} - 59..77 'async ....await': {unknown} - 65..71 '{ () }': () - 67..69 '()': () - 89..90 'z': {unknown} - 93..103 'try { () }': {unknown} - 97..103 '{ () }': () - 99..101 '()': () - 113..114 't': i32 - 121..127 '{ 92 }': i32 - 123..125 '92': i32 + 16..136 '{ ...2 }; }': () + 26..27 'x': i32 + 30..43 'unsafe { 92 }': i32 + 37..43 '{ 92 }': i32 + 39..41 '92': i32 + 53..54 'y': impl Future + 57..85 'async ...wait }': impl Future + 63..85 '{ asyn...wait }': () + 65..77 'async { () }': impl Future + 65..83 'async ....await': () + 71..77 '{ () }': () + 73..75 '()': () + 95..96 'z': {unknown} + 99..109 'try { () }': {unknown} + 103..109 '{ () }': () + 105..107 '()': () + 119..120 't': i32 + 127..133 '{ 92 }': i32 + 129..131 '92': i32 "#]], ) } diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 1f1056962..41d097519 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs @@ -85,6 +85,46 @@ mod future { ); } +#[test] +fn infer_async_block() { + check_types( + r#" +//- /main.rs crate:main deps:core +async fn test() { + let a = async { 42 }; + a; +// ^ impl Future + let x = a.await; + x; +// ^ i32 + let b = async {}.await; + b; +// ^ () + let c = async { + let y = Option::None; + y + // ^ Option + }; + let _: Option = c.await; + c; +// ^ impl Future> +} + +enum Option { None, Some(T) } + +//- /core.rs crate:core +#[prelude_import] use future::*; +mod future { + #[lang = "future_trait"] + trait Future { + type Output; + } +} + +"#, + ); +} + #[test] fn infer_try() { check_types( diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index 17c83b6a4..25e8ac186 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs @@ -11,6 +11,7 @@ use hir_def::{ lang_item::{lang_attr, LangItemTarget}, AssocContainerId, AssocItemId, HasModule, Lookup, TypeAliasId, }; +use hir_expand::name::name; use super::ChalkContext; use crate::{ @@ -18,7 +19,8 @@ use crate::{ display::HirDisplay, method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, utils::generics, - CallableDefId, DebruijnIndex, FnSig, GenericPredicate, Substs, Ty, TypeCtor, + BoundVar, CallableDefId, DebruijnIndex, FnSig, GenericPredicate, ProjectionPredicate, + ProjectionTy, Substs, TraitRef, Ty, TypeCtor, }; use mapping::{ convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue, @@ -166,27 +168,86 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId) -> Arc { let interned_id = crate::db::InternedOpaqueTyId::from(id); let full_id = self.db.lookup_intern_impl_trait_id(interned_id); - let (func, idx) = match full_id { - crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx), - }; - let datas = - self.db.return_type_impl_traits(func).expect("impl trait id without impl traits"); - let data = &datas.value.impl_traits[idx as usize]; - let bound = OpaqueTyDatumBound { - bounds: make_binders( - data.bounds - .value - .iter() - .cloned() - .filter(|b| !b.is_error()) - .map(|b| b.to_chalk(self.db)) - .collect(), - 1, - ), - where_clauses: make_binders(vec![], 0), + let bound = match full_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + let datas = self + .db + .return_type_impl_traits(func) + .expect("impl trait id without impl traits"); + let data = &datas.value.impl_traits[idx as usize]; + let bound = OpaqueTyDatumBound { + bounds: make_binders( + data.bounds + .value + .iter() + .cloned() + .filter(|b| !b.is_error()) + .map(|b| b.to_chalk(self.db)) + .collect(), + 1, + ), + where_clauses: make_binders(vec![], 0), + }; + let num_vars = datas.num_binders; + make_binders(bound, num_vars) + } + crate::OpaqueTyId::AsyncBlockTypeImplTrait(..) => { + if let Some((future_trait, future_output)) = self + .db + .lang_item(self.krate, "future_trait".into()) + .and_then(|item| item.as_trait()) + .and_then(|trait_| { + let alias = + self.db.trait_data(trait_).associated_type_by_name(&name![Output])?; + Some((trait_, alias)) + }) + { + // AsyncBlock: Future + // This is required by `fn impls_future` to check if we need to provide `.await` completion. + let impl_bound = GenericPredicate::Implemented(TraitRef { + trait_: future_trait, + // Self type as the first parameter. + substs: Substs::single(Ty::Bound(BoundVar { + debruijn: DebruijnIndex::INNERMOST, + index: 0, + })), + }); + // AsyncBlock: Future; + // debruijn: ^1 ^0 + let proj_bound = GenericPredicate::Projection(ProjectionPredicate { + // The parameter of the opaque type. + ty: Ty::Bound(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 }), + projection_ty: ProjectionTy { + associated_ty: future_output, + // Self type as the first parameter. + parameters: Substs::single(Ty::Bound(BoundVar::new( + DebruijnIndex::INNERMOST, + 0, + ))), + }, + }); + let bound = OpaqueTyDatumBound { + bounds: make_binders( + vec![impl_bound.to_chalk(self.db), proj_bound.to_chalk(self.db)], + 1, + ), + where_clauses: make_binders(vec![], 0), + }; + // The opaque type has 1 parameter. + make_binders(bound, 1) + } else { + // If failed to find `Future::Output`, return empty bounds as fallback. + let bound = OpaqueTyDatumBound { + bounds: make_binders(vec![], 0), + where_clauses: make_binders(vec![], 0), + }; + // The opaque type has 1 parameter. + make_binders(bound, 1) + } + } }; - let num_vars = datas.num_binders; - Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) }) + + Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound }) } fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { diff --git a/crates/hir_ty/src/traits/chalk/tls.rs b/crates/hir_ty/src/traits/chalk/tls.rs index db915625c..cb6b0fe81 100644 --- a/crates/hir_ty/src/traits/chalk/tls.rs +++ b/crates/hir_ty/src/traits/chalk/tls.rs @@ -73,6 +73,9 @@ impl DebugContext<'_> { crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { write!(f, "{{impl trait {} of {:?}}}", idx, func)?; } + crate::OpaqueTyId::AsyncBlockTypeImplTrait(def, idx) => { + write!(f, "{{impl trait of async block {} of {:?}}}", idx.into_raw(), def)?; + } }, TypeCtor::Closure { def, expr } => { write!(f, "{{closure {:?} in ", expr.into_raw())?; diff --git a/crates/ide/src/completion/complete_keyword.rs b/crates/ide/src/completion/complete_keyword.rs index 53ba76e0e..5645b41fa 100644 --- a/crates/ide/src/completion/complete_keyword.rs +++ b/crates/ide/src/completion/complete_keyword.rs @@ -506,6 +506,28 @@ pub mod future { #[lang = "future_trait"] pub trait Future {} } +"#, + expect![[r#" + kw await expr.await + "#]], + ); + + check( + r#" +//- /main.rs +use std::future::*; +fn foo() { + let a = async {}; + a.<|> +} + +//- /std/lib.rs +pub mod future { + #[lang = "future_trait"] + pub trait Future { + type Output; + } +} "#, expect![[r#" kw await expr.await -- cgit v1.2.3 From cc4e287bb519565e1d6d79d6e9bba19adbe9cc77 Mon Sep 17 00:00:00 2001 From: oxalica Date: Sat, 12 Sep 2020 00:12:42 +0800 Subject: Fix and prettify comments --- crates/hir_ty/src/traits/chalk.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'crates') diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index 25e8ac186..ff364789e 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs @@ -202,8 +202,12 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { Some((trait_, alias)) }) { - // AsyncBlock: Future - // This is required by `fn impls_future` to check if we need to provide `.await` completion. + // Making up `AsyncBlock: Future` + // + // |--------------------OpaqueTyDatum-------------------| + // |-------------OpaqueTyDatumBound--------------| + // for [Future, Future::Output = T] + // ^1 ^0 ^0 ^0 ^1 let impl_bound = GenericPredicate::Implemented(TraitRef { trait_: future_trait, // Self type as the first parameter. @@ -212,8 +216,6 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { index: 0, })), }); - // AsyncBlock: Future; - // debruijn: ^1 ^0 let proj_bound = GenericPredicate::Projection(ProjectionPredicate { // The parameter of the opaque type. ty: Ty::Bound(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 }), -- cgit v1.2.3 From 529c369c9bc15a73e7a03260eca84ccef99ac281 Mon Sep 17 00:00:00 2001 From: oxalica Date: Sat, 12 Sep 2020 01:03:28 +0800 Subject: Fix type walking about type of async block --- crates/hir/src/code_model.rs | 5 ++++ crates/hir_ty/src/lib.rs | 23 +++++++--------- crates/ide/src/hover.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 14 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 613b35afd..7a9747fc7 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs @@ -1602,6 +1602,11 @@ impl Type { cb(type_.derived(ty.clone())); } } + TypeCtor::OpaqueType(..) => { + if let Some(bounds) = ty.impl_trait_bounds(db) { + walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); + } + } _ => (), } diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 4a7d3a0d9..f16d1fc97 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -33,7 +33,6 @@ use hir_def::{ AdtId, AssocContainerId, DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, }; -use hir_expand::name::name; use itertools::Itertools; use crate::{ @@ -848,26 +847,22 @@ impl Ty { pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option> { match self { - Ty::Apply(ApplicationTy { ctor: TypeCtor::OpaqueType(opaque_ty_id), parameters }) => { + Ty::Apply(ApplicationTy { ctor: TypeCtor::OpaqueType(opaque_ty_id), .. }) => { match opaque_ty_id { OpaqueTyId::AsyncBlockTypeImplTrait(def, _expr) => { let krate = def.module(db.upcast()).krate; - if let Some(future_output) = db + if let Some(future_trait) = db .lang_item(krate, "future_trait".into()) .and_then(|item| item.as_trait()) - .and_then(|trait_| { - db.trait_data(trait_).associated_type_by_name(&name![Output]) - }) { - let proj = GenericPredicate::Projection(ProjectionPredicate { - projection_ty: ProjectionTy { - associated_ty: future_output, - // Self type. - parameters: Substs::single(self.clone()), - }, - ty: parameters[0].clone(), + // This is only used by type walking. + // Parameters will be walked outside, and projection predicate is not used. + // So just provide the Future trait. + let impl_bound = GenericPredicate::Implemented(TraitRef { + trait_: future_trait, + substs: Substs::empty(), }); - Some(vec![proj]) + Some(vec![impl_bound]) } else { None } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index efec0184e..37171cbef 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -2646,6 +2646,70 @@ fn foo(ar<|>g: &impl Foo + Bar) {} ); } + #[test] + fn test_hover_async_block_impl_trait_has_goto_type_action() { + check_actions( + r#" +struct S; +fn foo() { + let fo<|>o = async { S }; +} + +#[prelude_import] use future::*; +mod future { + #[lang = "future_trait"] + pub trait Future { type Output; } +} +"#, + expect![[r#" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::future::Future", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 101..163, + focus_range: Some( + 140..146, + ), + name: "Future", + kind: TRAIT, + container_name: None, + description: Some( + "pub trait Future", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "test::S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..9, + focus_range: Some( + 7..8, + ), + name: "S", + kind: STRUCT, + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + ], + ), + ] + "#]], + ); + } + #[test] fn test_hover_arg_generic_impl_trait_has_goto_type_action() { check_actions( -- cgit v1.2.3