Auto merge of #12986 - Veykril:completions, r=Veykril
Fix pattern field completions not working for unions
This commit is contained in:
commit
d186986af2
@ -617,7 +617,6 @@ pub(super) fn complete_name_ref(
|
|||||||
|
|
||||||
dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
|
dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
|
||||||
item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
|
item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
|
||||||
record::complete_record_expr_func_update(acc, ctx, path_ctx, expr_ctx);
|
|
||||||
snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
|
snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
|
||||||
}
|
}
|
||||||
PathKind::Type { location } => {
|
PathKind::Type { location } => {
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
//! Completion of names from the current scope in expression position.
|
//! Completion of names from the current scope in expression position.
|
||||||
|
|
||||||
use hir::ScopeDef;
|
use hir::ScopeDef;
|
||||||
|
use syntax::ast;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
completions::record::add_default_update,
|
||||||
context::{ExprCtx, PathCompletionCtx, Qualified},
|
context::{ExprCtx, PathCompletionCtx, Qualified},
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
@ -219,60 +221,78 @@ pub(crate) fn complete_expr_path(
|
|||||||
_ => (),
|
_ => (),
|
||||||
});
|
});
|
||||||
|
|
||||||
if is_func_update.is_none() {
|
match is_func_update {
|
||||||
let mut add_keyword =
|
Some(record_expr) => {
|
||||||
|kw, snippet| acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet);
|
let ty = ctx.sema.type_of_expr(&ast::Expr::RecordExpr(record_expr.clone()));
|
||||||
|
|
||||||
if !in_block_expr {
|
match ty.as_ref().and_then(|t| t.original.as_adt()) {
|
||||||
add_keyword("unsafe", "unsafe {\n $0\n}");
|
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("match", "match $1 {\n $0\n}");
|
None => {
|
||||||
add_keyword("while", "while $1 {\n $0\n}");
|
let mut add_keyword = |kw, snippet| {
|
||||||
add_keyword("while let", "while let $1 = $2 {\n $0\n}");
|
acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet)
|
||||||
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 in_condition || in_block_expr {
|
if !in_block_expr {
|
||||||
add_keyword("let", "let");
|
add_keyword("unsafe", "unsafe {\n $0\n}");
|
||||||
}
|
|
||||||
|
|
||||||
if after_if_expr {
|
|
||||||
add_keyword("else", "else {\n $0\n}");
|
|
||||||
add_keyword("else if", "else if $1 {\n $0\n}");
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
}
|
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 let Some(ty) = innermost_ret_ty {
|
if in_condition || in_block_expr {
|
||||||
add_keyword(
|
add_keyword("let", "let");
|
||||||
"return",
|
}
|
||||||
match (in_block_expr, ty.is_unit()) {
|
|
||||||
(true, true) => "return ;",
|
if after_if_expr {
|
||||||
(true, false) => "return;",
|
add_keyword("else", "else {\n $0\n}");
|
||||||
(false, true) => "return $0",
|
add_keyword("else if", "else if $1 {\n $0\n}");
|
||||||
(false, false) => "return",
|
}
|
||||||
},
|
|
||||||
);
|
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",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use syntax::ast::{self, Expr};
|
use syntax::ast::{self, Expr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, PatternContext, Qualified},
|
context::{DotAccess, DotAccessKind, PatternContext},
|
||||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
||||||
CompletionRelevancePostfixMatch, Completions,
|
CompletionRelevancePostfixMatch, Completions,
|
||||||
};
|
};
|
||||||
@ -14,7 +14,24 @@ pub(crate) fn complete_record_pattern_fields(
|
|||||||
pattern_ctx: &PatternContext,
|
pattern_ctx: &PatternContext,
|
||||||
) {
|
) {
|
||||||
if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
|
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);
|
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 {
|
if dot_prefix {
|
||||||
|
cov_mark::hit!(functional_update_one_dot);
|
||||||
let mut item =
|
let mut item =
|
||||||
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
|
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
|
||||||
item.insert_text(".");
|
item.insert_text(".");
|
||||||
@ -56,41 +78,18 @@ pub(crate) fn complete_record_expr_fields(
|
|||||||
complete_fields(acc, ctx, missing_fields);
|
complete_fields(acc, ctx, missing_fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This should probably be part of complete_path_expr
|
pub(crate) fn add_default_update(
|
||||||
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(
|
|
||||||
acc: &mut Completions,
|
acc: &mut Completions,
|
||||||
ctx: &CompletionContext<'_>,
|
ctx: &CompletionContext<'_>,
|
||||||
ty: Option<hir::TypeInfo>,
|
ty: Option<hir::TypeInfo>,
|
||||||
missing_fields: &[(hir::Field, hir::Type)],
|
|
||||||
) {
|
) {
|
||||||
let default_trait = ctx.famous_defs().core_default_Default();
|
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())
|
.zip(ty.as_ref())
|
||||||
.map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
|
.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
|
// 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 completion_text = "..Default::default()";
|
||||||
let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
|
let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
|
||||||
let completion_text =
|
let completion_text =
|
||||||
|
@ -134,6 +134,7 @@ pub(crate) struct ExprCtx {
|
|||||||
pub(crate) in_condition: bool,
|
pub(crate) in_condition: bool,
|
||||||
pub(crate) incomplete_let: bool,
|
pub(crate) incomplete_let: bool,
|
||||||
pub(crate) ref_expr_parent: Option<ast::RefExpr>,
|
pub(crate) ref_expr_parent: Option<ast::RefExpr>,
|
||||||
|
/// The surrounding RecordExpression we are completing a functional update
|
||||||
pub(crate) is_func_update: Option<ast::RecordExpr>,
|
pub(crate) is_func_update: Option<ast::RecordExpr>,
|
||||||
pub(crate) self_param: Option<hir::SelfParam>,
|
pub(crate) self_param: Option<hir::SelfParam>,
|
||||||
pub(crate) innermost_ret_ty: Option<hir::Type>,
|
pub(crate) innermost_ret_ty: Option<hir::Type>,
|
||||||
|
@ -103,47 +103,9 @@ fn foo(f: Struct) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn functional_update() {
|
fn in_functional_update() {
|
||||||
// FIXME: This should filter out all completions that do not have the type `Foo`
|
cov_mark::check!(functional_update);
|
||||||
check(
|
|
||||||
r#"
|
|
||||||
//- minicore:default
|
|
||||||
struct Foo { foo1: u32, foo2: u32 }
|
|
||||||
impl Default for Foo {
|
|
||||||
fn default() -> Self { loop {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let thing = 1;
|
|
||||||
let foo = Foo { foo1: 0, foo2: 0 };
|
|
||||||
let foo2 = Foo { thing, $0 }
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
expect![[r#"
|
|
||||||
fd ..Default::default()
|
|
||||||
fd foo1 u32
|
|
||||||
fd foo2 u32
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
check(
|
|
||||||
r#"
|
|
||||||
//- minicore:default
|
|
||||||
struct Foo { foo1: u32, foo2: u32 }
|
|
||||||
impl Default for Foo {
|
|
||||||
fn default() -> Self { loop {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let thing = 1;
|
|
||||||
let foo = Foo { foo1: 0, foo2: 0 };
|
|
||||||
let foo2 = Foo { thing, .$0 }
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
expect![[r#"
|
|
||||||
fd ..Default::default()
|
|
||||||
sn ..
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
//- minicore:default
|
//- minicore:default
|
||||||
@ -192,6 +154,56 @@ fn default() (as Default) fn() -> Self
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn functional_update_no_dot() {
|
||||||
|
cov_mark::check!(functional_update_field);
|
||||||
|
// FIXME: This should filter out all completions that do not have the type `Foo`
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore:default
|
||||||
|
struct Foo { foo1: u32, foo2: u32 }
|
||||||
|
impl Default for Foo {
|
||||||
|
fn default() -> Self { loop {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let thing = 1;
|
||||||
|
let foo = Foo { foo1: 0, foo2: 0 };
|
||||||
|
let foo2 = Foo { thing, $0 }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fd ..Default::default()
|
||||||
|
fd foo1 u32
|
||||||
|
fd foo2 u32
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn functional_update_one_dot() {
|
||||||
|
cov_mark::check!(functional_update_one_dot);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore:default
|
||||||
|
struct Foo { foo1: u32, foo2: u32 }
|
||||||
|
impl Default for Foo {
|
||||||
|
fn default() -> Self { loop {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let thing = 1;
|
||||||
|
let foo = Foo { foo1: 0, foo2: 0 };
|
||||||
|
let foo2 = Foo { thing, .$0 }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fd ..Default::default()
|
||||||
|
sn ..
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_union_literal() {
|
fn empty_union_literal() {
|
||||||
check(
|
check(
|
||||||
|
Loading…
Reference in New Issue
Block a user