diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index c2c6921cbc3a..e86716d74c8e 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -18,14 +18,18 @@ use ra_syntax::{ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ - expr, expr::{ + self, scope::{ExprScopes, ScopeId}, BodySourceMap, }, ids::LocationCtx, + name, + path::{PathKind, PathSegment}, + ty::method_resolution::implements_trait, AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId, - MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty, + MacroDef, Module, ModuleDef, Name, Path, PerNs, Resolution, Resolver, Static, Struct, Trait, + Ty, }; /// Locates the module by `FileId`. Picks topmost module in the file. @@ -409,6 +413,33 @@ impl SourceAnalyzer { crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value) } + /// Checks that particular type `ty` implements `std::future::Future`. + /// This function is used in `.await` syntax completion. + pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool { + let std_future_path = Path { + kind: PathKind::Abs, + segments: vec![ + PathSegment { name: name::STD, args_and_bindings: None }, + PathSegment { name: name::FUTURE_MOD, args_and_bindings: None }, + PathSegment { name: name::FUTURE_TYPE, args_and_bindings: None }, + ], + }; + + let std_future_trait = + match self.resolver.resolve_path_segments(db, &std_future_path).into_fully_resolved() { + PerNs { types: Some(Resolution::Def(ModuleDef::Trait(trait_))), .. } => trait_, + _ => return false, + }; + + let krate = match self.resolver.krate() { + Some(krate) => krate, + _ => return false, + }; + + let canonical_ty = crate::ty::Canonical { value: ty, num_vars: 0 }; + implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait) + } + #[cfg(test)] pub(crate) fn body_source_map(&self) -> Arc { self.body_source_map.clone().unwrap() diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index d421bf9efeeb..88d012a74c32 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -255,6 +255,20 @@ fn iterate_inherent_methods( None } +pub(crate) fn implements_trait( + ty: &Canonical, + db: &impl HirDatabase, + resolver: &Resolver, + krate: Crate, + trait_: Trait, +) -> bool { + let env = lower::trait_env(db, resolver); + let goal = generic_implements_goal(db, env.clone(), trait_, ty.clone()); + let solution = db.trait_solve(krate, goal); + + solution.is_some() +} + impl Ty { // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplBlocks`. diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index 536ba36dff3e..d43ff2eeccec 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs @@ -1,19 +1,36 @@ use hir::{AdtDef, Ty, TypeCtor}; -use crate::completion::{CompletionContext, Completions}; +use crate::completion::completion_item::CompletionKind; +use crate::{ + completion::{completion_context::CompletionContext, completion_item::Completions}, + CompletionItem, +}; use rustc_hash::FxHashSet; -/// Complete dot accesses, i.e. fields or methods (currently only fields). +/// Complete dot accesses, i.e. fields or methods (and .await syntax). pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { - let receiver_ty = - match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) { - Some(it) => it, - None => return, - }; + let dot_receiver = match &ctx.dot_receiver { + Some(expr) => expr, + _ => return, + }; + + let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { + Some(ty) => ty, + _ => return, + }; + if !ctx.is_call { complete_fields(acc, ctx, receiver_ty.clone()); } - complete_methods(acc, ctx, receiver_ty); + complete_methods(acc, ctx, receiver_ty.clone()); + + // Suggest .await syntax for types that implement Future trait + if ctx.analyzer.impls_future(ctx.db, receiver_ty) { + CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") + .detail("expr.await") + .insert_text("await") + .add_to(acc); + } } fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { @@ -406,4 +423,36 @@ mod tests { "### ); } + + #[test] + fn test_completion_await_impls_future() { + assert_debug_snapshot_matches!( + do_completion( + r###" + //- /main.rs + use std::future::*; + struct A {} + impl Future for A {} + fn foo(a: A) { + a.<|> + } + + //- /std/lib.rs + pub mod future { + pub trait Future {} + } + "###, CompletionKind::Keyword), + @r###" + ⋮[ + ⋮ CompletionItem { + ⋮ label: "await", + ⋮ source_range: [74; 74), + ⋮ delete: [74; 74), + ⋮ insert: "await", + ⋮ detail: "expr.await", + ⋮ }, + ⋮] + "### + ) + } }