Merge #1634
1634: Implement .await completion for futures r=flodiebold a=eupn Closes #1263 with completion for `.await` syntax for types that are implementing `std::future::Future` trait. r? @flodiebold Co-authored-by: Evgenii P <eupn@protonmail.com>
This commit is contained in:
commit
4912cc35af
@ -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<BodySourceMap> {
|
||||
self.body_source_map.clone().unwrap()
|
||||
|
@ -255,6 +255,20 @@ fn iterate_inherent_methods<T>(
|
||||
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);
|
||||
|
||||
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`.
|
||||
|
@ -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",
|
||||
⋮ },
|
||||
⋮]
|
||||
"###
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user