scope::{ExprScopes, ScopeId},
BodySourceMap,
},
+ ty::method_resolution::implements_trait,
ids::LocationCtx,
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId,
MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty,
crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value)
}
+ /// Checks that particular type `ty` implements `std::future::Future` trait.
+ /// This function is used in `.await` syntax completion.
+ pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool {
+ // Search for std::future::Future trait in scope
+ let future_trait = self.resolver.traits_in_scope(db)
+ .into_iter()
+ .filter(|t| {
+ let std = t.module(db).parent(db)
+ .and_then(|m| m
+ .name(db)
+ .and_then(|n| Some(n.to_string() == "std")))
+ .unwrap_or(false);
+
+ let future = t.module(db).name(db)
+ .and_then(|n| Some(n.to_string() == "future"))
+ .unwrap_or(false);
+
+ let future_trait = t.name(db)
+ .and_then(|n| Some(n.to_string() == "Future"))
+ .unwrap_or(false);
+
+ std && future && future_trait
+ })
+ .nth(0);
+
+ if let Some(trait_) = future_trait {
+ let krate = self.resolver.krate();
+ if let Some(krate) = krate {
+ let canonical_ty = crate::ty::Canonical { value: ty, num_vars: 0 };
+ return implements_trait(&canonical_ty, db, &self.resolver, krate, trait_);
+ }
+ }
+
+ false
+ }
+
#[cfg(test)]
pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
self.body_source_map.clone().unwrap()
resolve::Resolver,
traits::TraitItem,
ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy},
- ty::{Ty, TypeCtor},
+ ty::{Ty, TypeCtor, traits::Solution},
Crate, Function, HirDatabase, Module, Name, Trait,
};
None
}
+pub(crate) fn implements_trait(ty: &Canonical<Ty>, 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);
+
+ if let Some(solution) = solution {
+ if let Solution::Unique(_) = solution {
+ return true
+ }
+ }
+
+ false
+}
+
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`.
use hir::{AdtDef, Ty, TypeCtor};
-use crate::completion::{CompletionContext, Completions};
+use crate::{completion::{
+ completion_context::CompletionContext,
+ completion_item::Completions,
+}, CompletionItem};
+use ra_syntax::ast::AstNode;
+use ra_text_edit::TextEditBuilder;
use rustc_hash::FxHashSet;
+use crate::completion::completion_item::{Builder, CompletionKind};
+use ra_syntax::TextRange;
-/// Complete dot accesses, i.e. fields or methods (currently only fields).
+/// Applies postfix edition but with CompletionKind::Reference
+fn postfix_reference(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
+ let edit = {
+ let receiver_range =
+ ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
+ let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
+ let mut builder = TextEditBuilder::default();
+ builder.replace(delete_range, snippet.to_string());
+ builder.finish()
+ };
+ CompletionItem::new(CompletionKind::Reference, ctx.source_range(), label)
+ .detail(detail)
+ .snippet_edit(edit)
+}
+
+/// 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,
- };
- if !ctx.is_call {
- complete_fields(acc, ctx, receiver_ty.clone());
+ if let Some(dot_receiver) = &ctx.dot_receiver {
+ let receiver_text = dot_receiver.syntax().text().to_string();
+ let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
+
+ if let Some(receiver_ty) = receiver_ty {
+ if !ctx.is_call {
+ complete_fields(acc, ctx, receiver_ty.clone());
+ }
+ complete_methods(acc, ctx, receiver_ty.clone());
+
+ // Suggest .await syntax for types that implement std::future::Future
+ if ctx.analyzer.impls_future(ctx.db, receiver_ty) {
+ postfix_reference(ctx, ".await", "expr.await", &format!("{}.await", receiver_text))
+ .add_to(acc);
+ }
+ }
}
- complete_methods(acc, ctx, receiver_ty);
}
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
"###
);
}
+
+ #[test]
+ fn test_completion_await_impls_future() {
+ assert_debug_snapshot_matches!(
+ do_ref_completion(
+ r"
+ // Mock Future trait from stdlib
+ pub mod std { pub mod future { pub trait Future {} } }
+
+ use std::future::*;
+ struct A {}
+ impl Future for A {}
+
+ fn foo(a: A) {
+ a.<|>
+ }
+ "),
+ @r###"
+ ⋮[
+ ⋮ CompletionItem {
+ ⋮ label: ".await",
+ ⋮ source_range: [249; 249),
+ ⋮ delete: [247; 249),
+ ⋮ insert: "a.await",
+ ⋮ detail: "expr.await",
+ ⋮ },
+ ⋮]
+ "###
+ )
+ }
}