Skip to content

Commit 4912cc3

Browse files
bors[bot]eupn
andcommitted
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 <[email protected]>
2 parents 658382c + 9f9c5aa commit 4912cc3

File tree

3 files changed

+104
-10
lines changed

3 files changed

+104
-10
lines changed

crates/ra_hir/src/source_binder.rs

+33-2
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@ use ra_syntax::{
1818
use rustc_hash::{FxHashMap, FxHashSet};
1919

2020
use crate::{
21-
expr,
2221
expr::{
22+
self,
2323
scope::{ExprScopes, ScopeId},
2424
BodySourceMap,
2525
},
2626
ids::LocationCtx,
27+
name,
28+
path::{PathKind, PathSegment},
29+
ty::method_resolution::implements_trait,
2730
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId,
28-
MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty,
31+
MacroDef, Module, ModuleDef, Name, Path, PerNs, Resolution, Resolver, Static, Struct, Trait,
32+
Ty,
2933
};
3034

3135
/// Locates the module by `FileId`. Picks topmost module in the file.
@@ -409,6 +413,33 @@ impl SourceAnalyzer {
409413
crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value)
410414
}
411415

416+
/// Checks that particular type `ty` implements `std::future::Future`.
417+
/// This function is used in `.await` syntax completion.
418+
pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool {
419+
let std_future_path = Path {
420+
kind: PathKind::Abs,
421+
segments: vec![
422+
PathSegment { name: name::STD, args_and_bindings: None },
423+
PathSegment { name: name::FUTURE_MOD, args_and_bindings: None },
424+
PathSegment { name: name::FUTURE_TYPE, args_and_bindings: None },
425+
],
426+
};
427+
428+
let std_future_trait =
429+
match self.resolver.resolve_path_segments(db, &std_future_path).into_fully_resolved() {
430+
PerNs { types: Some(Resolution::Def(ModuleDef::Trait(trait_))), .. } => trait_,
431+
_ => return false,
432+
};
433+
434+
let krate = match self.resolver.krate() {
435+
Some(krate) => krate,
436+
_ => return false,
437+
};
438+
439+
let canonical_ty = crate::ty::Canonical { value: ty, num_vars: 0 };
440+
implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait)
441+
}
442+
412443
#[cfg(test)]
413444
pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
414445
self.body_source_map.clone().unwrap()

crates/ra_hir/src/ty/method_resolution.rs

+14
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,20 @@ fn iterate_inherent_methods<T>(
255255
None
256256
}
257257

258+
pub(crate) fn implements_trait(
259+
ty: &Canonical<Ty>,
260+
db: &impl HirDatabase,
261+
resolver: &Resolver,
262+
krate: Crate,
263+
trait_: Trait,
264+
) -> bool {
265+
let env = lower::trait_env(db, resolver);
266+
let goal = generic_implements_goal(db, env.clone(), trait_, ty.clone());
267+
let solution = db.trait_solve(krate, goal);
268+
269+
solution.is_some()
270+
}
271+
258272
impl Ty {
259273
// This would be nicer if it just returned an iterator, but that runs into
260274
// lifetime problems, because we need to borrow temp `CrateImplBlocks`.

crates/ra_ide_api/src/completion/complete_dot.rs

+57-8
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
11
use hir::{AdtDef, Ty, TypeCtor};
22

3-
use crate::completion::{CompletionContext, Completions};
3+
use crate::completion::completion_item::CompletionKind;
4+
use crate::{
5+
completion::{completion_context::CompletionContext, completion_item::Completions},
6+
CompletionItem,
7+
};
48
use rustc_hash::FxHashSet;
59

6-
/// Complete dot accesses, i.e. fields or methods (currently only fields).
10+
/// Complete dot accesses, i.e. fields or methods (and .await syntax).
711
pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
8-
let receiver_ty =
9-
match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) {
10-
Some(it) => it,
11-
None => return,
12-
};
12+
let dot_receiver = match &ctx.dot_receiver {
13+
Some(expr) => expr,
14+
_ => return,
15+
};
16+
17+
let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) {
18+
Some(ty) => ty,
19+
_ => return,
20+
};
21+
1322
if !ctx.is_call {
1423
complete_fields(acc, ctx, receiver_ty.clone());
1524
}
16-
complete_methods(acc, ctx, receiver_ty);
25+
complete_methods(acc, ctx, receiver_ty.clone());
26+
27+
// Suggest .await syntax for types that implement Future trait
28+
if ctx.analyzer.impls_future(ctx.db, receiver_ty) {
29+
CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
30+
.detail("expr.await")
31+
.insert_text("await")
32+
.add_to(acc);
33+
}
1734
}
1835

1936
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
@@ -406,4 +423,36 @@ mod tests {
406423
"###
407424
);
408425
}
426+
427+
#[test]
428+
fn test_completion_await_impls_future() {
429+
assert_debug_snapshot_matches!(
430+
do_completion(
431+
r###"
432+
//- /main.rs
433+
use std::future::*;
434+
struct A {}
435+
impl Future for A {}
436+
fn foo(a: A) {
437+
a.<|>
438+
}
439+
440+
//- /std/lib.rs
441+
pub mod future {
442+
pub trait Future {}
443+
}
444+
"###, CompletionKind::Keyword),
445+
@r###"
446+
⋮[
447+
⋮ CompletionItem {
448+
⋮ label: "await",
449+
⋮ source_range: [74; 74),
450+
⋮ delete: [74; 74),
451+
⋮ insert: "await",
452+
⋮ detail: "expr.await",
453+
⋮ },
454+
⋮]
455+
"###
456+
)
457+
}
409458
}

0 commit comments

Comments
 (0)