aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/source_binder.rs37
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs16
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs80
3 files changed, 122 insertions, 11 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index c2c6921cb..8496b143a 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -23,6 +23,7 @@ use crate::{
23 scope::{ExprScopes, ScopeId}, 23 scope::{ExprScopes, ScopeId},
24 BodySourceMap, 24 BodySourceMap,
25 }, 25 },
26 ty::method_resolution::implements_trait,
26 ids::LocationCtx, 27 ids::LocationCtx,
27 AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId, 28 AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId,
28 MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty, 29 MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty,
@@ -409,6 +410,42 @@ impl SourceAnalyzer {
409 crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value) 410 crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value)
410 } 411 }
411 412
413 /// Checks that particular type `ty` implements `std::future::Future` trait.
414 /// This function is used in `.await` syntax completion.
415 pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool {
416 // Search for std::future::Future trait in scope
417 let future_trait = self.resolver.traits_in_scope(db)
418 .into_iter()
419 .filter(|t| {
420 let std = t.module(db).parent(db)
421 .and_then(|m| m
422 .name(db)
423 .and_then(|n| Some(n.to_string() == "std")))
424 .unwrap_or(false);
425
426 let future = t.module(db).name(db)
427 .and_then(|n| Some(n.to_string() == "future"))
428 .unwrap_or(false);
429
430 let future_trait = t.name(db)
431 .and_then(|n| Some(n.to_string() == "Future"))
432 .unwrap_or(false);
433
434 std && future && future_trait
435 })
436 .nth(0);
437
438 if let Some(trait_) = future_trait {
439 let krate = self.resolver.krate();
440 if let Some(krate) = krate {
441 let canonical_ty = crate::ty::Canonical { value: ty, num_vars: 0 };
442 return implements_trait(&canonical_ty, db, &self.resolver, krate, trait_);
443 }
444 }
445
446 false
447 }
448
412 #[cfg(test)] 449 #[cfg(test)]
413 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { 450 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
414 self.body_source_map.clone().unwrap() 451 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 d421bf9ef..2e2f88138 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -15,7 +15,7 @@ use crate::{
15 resolve::Resolver, 15 resolve::Resolver,
16 traits::TraitItem, 16 traits::TraitItem,
17 ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy}, 17 ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy},
18 ty::{Ty, TypeCtor}, 18 ty::{Ty, TypeCtor, traits::Solution},
19 Crate, Function, HirDatabase, Module, Name, Trait, 19 Crate, Function, HirDatabase, Module, Name, Trait,
20}; 20};
21 21
@@ -255,6 +255,20 @@ fn iterate_inherent_methods<T>(
255 None 255 None
256} 256}
257 257
258pub(crate) fn implements_trait(ty: &Canonical<Ty>, db: &impl HirDatabase, resolver: &Resolver, krate: Crate, trait_: Trait) -> bool {
259 let env = lower::trait_env(db, resolver);
260 let goal = generic_implements_goal(db, env.clone(), trait_, ty.clone());
261 let solution = db.trait_solve(krate, goal);
262
263 if let Some(solution) = solution {
264 if let Solution::Unique(_) = solution {
265 return true
266 }
267 }
268
269 false
270}
271
258impl Ty { 272impl Ty {
259 // This would be nicer if it just returned an iterator, but that runs into 273 // This would be nicer if it just returned an iterator, but that runs into
260 // lifetime problems, because we need to borrow temp `CrateImplBlocks`. 274 // 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 536ba36df..b6579be63 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -1,19 +1,49 @@
1use hir::{AdtDef, Ty, TypeCtor}; 1use hir::{AdtDef, Ty, TypeCtor};
2 2
3use crate::completion::{CompletionContext, Completions}; 3use crate::{completion::{
4 completion_context::CompletionContext,
5 completion_item::Completions,
6}, CompletionItem};
7use ra_syntax::ast::AstNode;
8use ra_text_edit::TextEditBuilder;
4use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
10use crate::completion::completion_item::{Builder, CompletionKind};
11use ra_syntax::TextRange;
5 12
6/// Complete dot accesses, i.e. fields or methods (currently only fields). 13/// Applies postfix edition but with CompletionKind::Reference
14fn postfix_reference(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
15 let edit = {
16 let receiver_range =
17 ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
18 let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
19 let mut builder = TextEditBuilder::default();
20 builder.replace(delete_range, snippet.to_string());
21 builder.finish()
22 };
23 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), label)
24 .detail(detail)
25 .snippet_edit(edit)
26}
27
28/// Complete dot accesses, i.e. fields or methods (and .await syntax).
7pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 29pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
8 let receiver_ty = 30 if let Some(dot_receiver) = &ctx.dot_receiver {
9 match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) { 31 let receiver_text = dot_receiver.syntax().text().to_string();
10 Some(it) => it, 32 let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
11 None => return, 33
12 }; 34 if let Some(receiver_ty) = receiver_ty {
13 if !ctx.is_call { 35 if !ctx.is_call {
14 complete_fields(acc, ctx, receiver_ty.clone()); 36 complete_fields(acc, ctx, receiver_ty.clone());
37 }
38 complete_methods(acc, ctx, receiver_ty.clone());
39
40 // Suggest .await syntax for types that implement std::future::Future
41 if ctx.analyzer.impls_future(ctx.db, receiver_ty) {
42 postfix_reference(ctx, ".await", "expr.await", &format!("{}.await", receiver_text))
43 .add_to(acc);
44 }
45 }
15 } 46 }
16 complete_methods(acc, ctx, receiver_ty);
17} 47}
18 48
19fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { 49fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
@@ -406,4 +436,34 @@ mod tests {
406 "### 436 "###
407 ); 437 );
408 } 438 }
439
440 #[test]
441 fn test_completion_await_impls_future() {
442 assert_debug_snapshot_matches!(
443 do_ref_completion(
444 r"
445 // Mock Future trait from stdlib
446 pub mod std { pub mod future { pub trait Future {} } }
447
448 use std::future::*;
449 struct A {}
450 impl Future for A {}
451
452 fn foo(a: A) {
453 a.<|>
454 }
455 "),
456 @r###"
457 ⋮[
458 ⋮ CompletionItem {
459 ⋮ label: ".await",
460 ⋮ source_range: [249; 249),
461 ⋮ delete: [247; 249),
462 ⋮ insert: "a.await",
463 ⋮ detail: "expr.await",
464 ⋮ },
465 ⋮]
466 "###
467 )
468 }
409} 469}