mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
Implement completion for the .await syntax
This commit is contained in:
parent
831d39b0f7
commit
c417b98f02
3 changed files with 122 additions and 11 deletions
|
@ -23,6 +23,7 @@ use crate::{
|
||||||
scope::{ExprScopes, ScopeId},
|
scope::{ExprScopes, ScopeId},
|
||||||
BodySourceMap,
|
BodySourceMap,
|
||||||
},
|
},
|
||||||
|
ty::method_resolution::implements_trait,
|
||||||
ids::LocationCtx,
|
ids::LocationCtx,
|
||||||
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId,
|
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId,
|
||||||
MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty,
|
MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty,
|
||||||
|
@ -409,6 +410,42 @@ impl SourceAnalyzer {
|
||||||
crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value)
|
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)]
|
#[cfg(test)]
|
||||||
pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
|
pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
|
||||||
self.body_source_map.clone().unwrap()
|
self.body_source_map.clone().unwrap()
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
resolve::Resolver,
|
resolve::Resolver,
|
||||||
traits::TraitItem,
|
traits::TraitItem,
|
||||||
ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy},
|
ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy},
|
||||||
ty::{Ty, TypeCtor},
|
ty::{Ty, TypeCtor, traits::Solution},
|
||||||
Crate, Function, HirDatabase, Module, Name, Trait,
|
Crate, Function, HirDatabase, Module, Name, Trait,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -255,6 +255,20 @@ fn iterate_inherent_methods<T>(
|
||||||
None
|
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 {
|
impl Ty {
|
||||||
// This would be nicer if it just returned an iterator, but that runs into
|
// This would be nicer if it just returned an iterator, but that runs into
|
||||||
// lifetime problems, because we need to borrow temp `CrateImplBlocks`.
|
// lifetime problems, because we need to borrow temp `CrateImplBlocks`.
|
||||||
|
|
|
@ -1,19 +1,49 @@
|
||||||
use hir::{AdtDef, Ty, TypeCtor};
|
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 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
|
||||||
pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
fn postfix_reference(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
|
||||||
let receiver_ty =
|
let edit = {
|
||||||
match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) {
|
let receiver_range =
|
||||||
Some(it) => it,
|
ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
|
||||||
None => return,
|
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) {
|
||||||
|
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 {
|
if !ctx.is_call {
|
||||||
complete_fields(acc, ctx, receiver_ty.clone());
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
|
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
|
||||||
|
@ -406,4 +436,34 @@ mod tests {
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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",
|
||||||
|
⋮ },
|
||||||
|
⋮]
|
||||||
|
"###
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue