summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-completion/src/completions
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-completion/src/completions')
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs124
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs70
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs69
7 files changed, 186 insertions, 106 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index cf40ca489..02004ff7b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -19,7 +19,7 @@ pub(crate) fn complete_dot(
};
// Suggest .await syntax for types that implement Future trait
- if receiver_ty.impls_future(ctx.db) {
+ if receiver_ty.impls_into_future(ctx.db) {
let mut item =
CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await");
item.detail("expr.await");
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 5d0ddaaf2..588b52cc1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -1,8 +1,10 @@
//! Completion of names from the current scope in expression position.
use hir::ScopeDef;
+use syntax::ast;
use crate::{
+ completions::record::add_default_update,
context::{ExprCtx, PathCompletionCtx, Qualified},
CompletionContext, Completions,
};
@@ -219,60 +221,90 @@ pub(crate) fn complete_expr_path(
_ => (),
});
- if is_func_update.is_none() {
- let mut add_keyword =
- |kw, snippet| acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet);
+ match is_func_update {
+ Some(record_expr) => {
+ let ty = ctx.sema.type_of_expr(&ast::Expr::RecordExpr(record_expr.clone()));
- if !in_block_expr {
- add_keyword("unsafe", "unsafe {\n $0\n}");
- }
- add_keyword("match", "match $1 {\n $0\n}");
- add_keyword("while", "while $1 {\n $0\n}");
- add_keyword("while let", "while let $1 = $2 {\n $0\n}");
- add_keyword("loop", "loop {\n $0\n}");
- if in_match_guard {
- add_keyword("if", "if $0");
- } else {
- add_keyword("if", "if $1 {\n $0\n}");
+ match ty.as_ref().and_then(|t| t.original.as_adt()) {
+ Some(hir::Adt::Union(_)) => (),
+ _ => {
+ cov_mark::hit!(functional_update);
+ let missing_fields =
+ ctx.sema.record_literal_missing_fields(record_expr);
+ if !missing_fields.is_empty() {
+ add_default_update(acc, ctx, ty);
+ }
+ }
+ };
}
- add_keyword("if let", "if let $1 = $2 {\n $0\n}");
- add_keyword("for", "for $1 in $2 {\n $0\n}");
- add_keyword("true", "true");
- add_keyword("false", "false");
+ None => {
+ let mut add_keyword = |kw, snippet| {
+ acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet)
+ };
- if in_condition || in_block_expr {
- add_keyword("let", "let");
- }
+ if !in_block_expr {
+ add_keyword("unsafe", "unsafe {\n $0\n}");
+ }
+ add_keyword("match", "match $1 {\n $0\n}");
+ add_keyword("while", "while $1 {\n $0\n}");
+ add_keyword("while let", "while let $1 = $2 {\n $0\n}");
+ add_keyword("loop", "loop {\n $0\n}");
+ if in_match_guard {
+ add_keyword("if", "if $0");
+ } else {
+ add_keyword("if", "if $1 {\n $0\n}");
+ }
+ add_keyword("if let", "if let $1 = $2 {\n $0\n}");
+ add_keyword("for", "for $1 in $2 {\n $0\n}");
+ add_keyword("true", "true");
+ add_keyword("false", "false");
- if after_if_expr {
- add_keyword("else", "else {\n $0\n}");
- add_keyword("else if", "else if $1 {\n $0\n}");
- }
+ if in_condition || in_block_expr {
+ add_keyword("let", "let");
+ }
- if wants_mut_token {
- add_keyword("mut", "mut ");
- }
+ if after_if_expr {
+ add_keyword("else", "else {\n $0\n}");
+ add_keyword("else if", "else if $1 {\n $0\n}");
+ }
- if in_loop_body {
- if in_block_expr {
- add_keyword("continue", "continue;");
- add_keyword("break", "break;");
- } else {
- add_keyword("continue", "continue");
- add_keyword("break", "break");
+ if wants_mut_token {
+ add_keyword("mut", "mut ");
+ }
+
+ if in_loop_body {
+ if in_block_expr {
+ add_keyword("continue", "continue;");
+ add_keyword("break", "break;");
+ } else {
+ add_keyword("continue", "continue");
+ add_keyword("break", "break");
+ }
}
- }
- if let Some(ty) = innermost_ret_ty {
- add_keyword(
- "return",
- match (in_block_expr, ty.is_unit()) {
- (true, true) => "return ;",
- (true, false) => "return;",
- (false, true) => "return $0",
- (false, false) => "return",
- },
- );
+ if let Some(ret_ty) = innermost_ret_ty {
+ add_keyword(
+ "return",
+ match (ret_ty.is_unit(), in_block_expr) {
+ (true, true) => {
+ cov_mark::hit!(return_unit_block);
+ "return;"
+ }
+ (true, false) => {
+ cov_mark::hit!(return_unit_no_block);
+ "return"
+ }
+ (false, true) => {
+ cov_mark::hit!(return_value_block);
+ "return $0;"
+ }
+ (false, false) => {
+ cov_mark::hit!(return_value_no_block);
+ "return $0"
+ }
+ },
+ );
+ }
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index e9256803c..785db6fde 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -233,7 +233,8 @@ fn add_type_alias_impl(
type_alias: hir::TypeAlias,
) {
let alias_name = type_alias.name(ctx.db);
- let (alias_name, escaped_name) = (alias_name.to_smol_str(), alias_name.escaped().to_smol_str());
+ let (alias_name, escaped_name) =
+ (alias_name.unescaped().to_smol_str(), alias_name.to_smol_str());
let label = format!("type {} =", alias_name);
let replacement = format!("type {} = ", escaped_name);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
index 3989a451b..1d03c8cc5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
@@ -75,16 +75,17 @@ impl Future for A {}
fn foo(a: A) { a.$0 }
"#,
expect![[r#"
- kw await expr.await
- sn box Box::new(expr)
- sn call function(expr)
- sn dbg dbg!(expr)
- sn dbgr dbg!(&expr)
- sn let let
- sn letm let mut
- sn match match expr {}
- sn ref &expr
- sn refm &mut expr
+ kw await expr.await
+ me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
"#]],
);
@@ -98,18 +99,45 @@ fn foo() {
}
"#,
expect![[r#"
- kw await expr.await
- sn box Box::new(expr)
- sn call function(expr)
- sn dbg dbg!(expr)
- sn dbgr dbg!(&expr)
- sn let let
- sn letm let mut
- sn match match expr {}
- sn ref &expr
- sn refm &mut expr
+ kw await expr.await
+ me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
"#]],
- )
+ );
+ }
+
+ #[test]
+ fn test_completion_await_impls_into_future() {
+ check(
+ r#"
+//- minicore: future
+use core::future::*;
+struct A {}
+impl IntoFuture for A {}
+fn foo(a: A) { a.$0 }
+"#,
+ expect![[r#"
+ kw await expr.await
+ me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
+ "#]],
+ );
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
index 9c975b929..950731eb4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
@@ -53,6 +53,7 @@ pub(crate) fn complete_mod(
let existing_mod_declarations = current_module
.children(ctx.db)
.filter_map(|module| Some(module.name(ctx.db)?.to_string()))
+ .filter(|module| module != ctx.original_token.text())
.collect::<FxHashSet<_>>();
let module_declaration_file =
@@ -351,4 +352,23 @@ fn ignored_bar() {}
"#]],
);
}
+
+ #[test]
+ fn semi_colon_completion() {
+ check(
+ r#"
+//- /lib.rs
+mod foo;
+//- /foo.rs
+mod bar {
+ mod baz$0
+}
+//- /foo/bar/baz.rs
+fn baz() {}
+"#,
+ expect![[r#"
+ md baz;
+ "#]],
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
index 6b94347e0..b273a4cb5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -173,7 +173,7 @@ impl FormatStrParser {
}
}
(State::Expr, ':') if chars.peek().copied() == Some(':') => {
- // path seperator
+ // path separator
current_expr.push_str("::");
chars.next();
}
@@ -185,7 +185,7 @@ impl FormatStrParser {
current_expr = String::new();
self.state = State::FormatOpts;
} else {
- // We're inside of braced expression, assume that it's a struct field name/value delimeter.
+ // We're inside of braced expression, assume that it's a struct field name/value delimiter.
current_expr.push(chr);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index 1c9042390..5d96fbd30 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -3,7 +3,7 @@ use ide_db::SymbolKind;
use syntax::ast::{self, Expr};
use crate::{
- context::{DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, PatternContext, Qualified},
+ context::{DotAccess, DotAccessKind, PatternContext},
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
CompletionRelevancePostfixMatch, Completions,
};
@@ -14,7 +14,24 @@ pub(crate) fn complete_record_pattern_fields(
pattern_ctx: &PatternContext,
) {
if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
- complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat));
+ let ty = ctx.sema.type_of_pat(&ast::Pat::RecordPat(record_pat.clone()));
+ let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
+ Some(hir::Adt::Union(un)) => {
+ // ctx.sema.record_pat_missing_fields will always return
+ // an empty Vec on a union literal. This is normally
+ // reasonable, but here we'd like to present the full list
+ // of fields if the literal is empty.
+ let were_fields_specified =
+ record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some();
+
+ match were_fields_specified {
+ false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
+ true => return,
+ }
+ }
+ _ => ctx.sema.record_pattern_missing_fields(record_pat),
+ };
+ complete_fields(acc, ctx, missing_fields);
}
}
@@ -42,8 +59,13 @@ pub(crate) fn complete_record_expr_fields(
}
_ => {
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
- add_default_update(acc, ctx, ty, &missing_fields);
+
+ if !missing_fields.is_empty() {
+ cov_mark::hit!(functional_update_field);
+ add_default_update(acc, ctx, ty);
+ }
if dot_prefix {
+ cov_mark::hit!(functional_update_one_dot);
let mut item =
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
item.insert_text(".");
@@ -56,41 +78,18 @@ pub(crate) fn complete_record_expr_fields(
complete_fields(acc, ctx, missing_fields);
}
-// FIXME: This should probably be part of complete_path_expr
-pub(crate) fn complete_record_expr_func_update(
- acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- path_ctx: &PathCompletionCtx,
- expr_ctx: &ExprCtx,
-) {
- if !matches!(path_ctx.qualified, Qualified::No) {
- return;
- }
- if let ExprCtx { is_func_update: Some(record_expr), .. } = expr_ctx {
- let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
-
- match ty.as_ref().and_then(|t| t.original.as_adt()) {
- Some(hir::Adt::Union(_)) => (),
- _ => {
- let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
- add_default_update(acc, ctx, ty, &missing_fields);
- }
- };
- }
-}
-
-fn add_default_update(
+pub(crate) fn add_default_update(
acc: &mut Completions,
ctx: &CompletionContext<'_>,
ty: Option<hir::TypeInfo>,
- missing_fields: &[(hir::Field, hir::Type)],
) {
let default_trait = ctx.famous_defs().core_default_Default();
- let impl_default_trait = default_trait
+ let impls_default_trait = default_trait
.zip(ty.as_ref())
.map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
- if impl_default_trait && !missing_fields.is_empty() {
+ if impls_default_trait {
// FIXME: This should make use of scope_def like completions so we get all the other goodies
+ // that is we should handle this like actually completing the default function
let completion_text = "..Default::default()";
let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
let completion_text =
@@ -130,7 +129,7 @@ mod tests {
#[test]
fn literal_struct_completion_edit() {
check_edit(
- "FooDesc {…}",
+ "FooDesc{}",
r#"
struct FooDesc { pub bar: bool }
@@ -155,7 +154,7 @@ fn baz() {
#[test]
fn literal_struct_impl_self_completion() {
check_edit(
- "Self {…}",
+ "Self{}",
r#"
struct Foo {
bar: u64,
@@ -181,7 +180,7 @@ impl Foo {
);
check_edit(
- "Self(…)",
+ "Self()",
r#"
mod submod {
pub struct Foo(pub u64);
@@ -210,7 +209,7 @@ impl submod::Foo {
#[test]
fn literal_struct_completion_from_sub_modules() {
check_edit(
- "submod::Struct {…}",
+ "submod::Struct{}",
r#"
mod submod {
pub struct Struct {
@@ -239,7 +238,7 @@ fn f() -> submod::Struct {
#[test]
fn literal_struct_complexion_module() {
check_edit(
- "FooDesc {…}",
+ "FooDesc{}",
r#"
mod _69latrick {
pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }