diff options
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 35 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 14 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_dot.rs | 65 |
3 files changed, 104 insertions, 10 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index c2c6921cb..e86716d74 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -18,14 +18,18 @@ use ra_syntax::{ | |||
18 | use rustc_hash::{FxHashMap, FxHashSet}; | 18 | use rustc_hash::{FxHashMap, FxHashSet}; |
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | expr, | ||
22 | expr::{ | 21 | expr::{ |
22 | self, | ||
23 | scope::{ExprScopes, ScopeId}, | 23 | scope::{ExprScopes, ScopeId}, |
24 | BodySourceMap, | 24 | BodySourceMap, |
25 | }, | 25 | }, |
26 | ids::LocationCtx, | 26 | ids::LocationCtx, |
27 | name, | ||
28 | path::{PathKind, PathSegment}, | ||
29 | ty::method_resolution::implements_trait, | ||
27 | AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId, | 30 | 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, | ||
29 | }; | 33 | }; |
30 | 34 | ||
31 | /// Locates the module by `FileId`. Picks topmost module in the file. | 35 | /// Locates the module by `FileId`. Picks topmost module in the file. |
@@ -409,6 +413,33 @@ impl SourceAnalyzer { | |||
409 | crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value) | 413 | crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value) |
410 | } | 414 | } |
411 | 415 | ||
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 | |||
412 | #[cfg(test)] | 443 | #[cfg(test)] |
413 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { | 444 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { |
414 | self.body_source_map.clone().unwrap() | 445 | 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..88d012a74 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<T>( | |||
255 | None | 255 | None |
256 | } | 256 | } |
257 | 257 | ||
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 | |||
258 | impl Ty { | 272 | impl 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..d43ff2eec 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 @@ | |||
1 | use hir::{AdtDef, Ty, TypeCtor}; | 1 | use hir::{AdtDef, Ty, TypeCtor}; |
2 | 2 | ||
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 | }; | ||
4 | use rustc_hash::FxHashSet; | 8 | use rustc_hash::FxHashSet; |
5 | 9 | ||
6 | /// Complete dot accesses, i.e. fields or methods (currently only fields). | 10 | /// Complete dot accesses, i.e. fields or methods (and .await syntax). |
7 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 11 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { |
8 | let receiver_ty = | 12 | let dot_receiver = match &ctx.dot_receiver { |
9 | match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) { | 13 | Some(expr) => expr, |
10 | Some(it) => it, | 14 | _ => return, |
11 | None => return, | 15 | }; |
12 | }; | 16 | |
17 | let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { | ||
18 | Some(ty) => ty, | ||
19 | _ => return, | ||
20 | }; | ||
21 | |||
13 | if !ctx.is_call { | 22 | if !ctx.is_call { |
14 | complete_fields(acc, ctx, receiver_ty.clone()); | 23 | complete_fields(acc, ctx, receiver_ty.clone()); |
15 | } | 24 | } |
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 | } | ||
17 | } | 34 | } |
18 | 35 | ||
19 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { | 36 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { |
@@ -406,4 +423,36 @@ mod tests { | |||
406 | "### | 423 | "### |
407 | ); | 424 | ); |
408 | } | 425 | } |
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 | } | ||
409 | } | 458 | } |