mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Fix pattern field completions not working for unions
This commit is contained in:
parent
e1e93c4438
commit
8c9359b072
5 changed files with 140 additions and 116 deletions
|
@ -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,76 @@ 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);
|
||||||
|
add_default_update(acc, ctx, ty, &missing_fields);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
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 ide_db::SymbolKind;
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +61,7 @@ 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);
|
add_default_update(acc, ctx, ty, &missing_fields);
|
||||||
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,30 +74,7 @@ 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>,
|
||||||
|
|
|
@ -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,55 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn functional_update_no_dot() {
|
||||||
|
// 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 a new issue