3779: Complete only missing fields in pats r=matklad a=SomeoneToIgnore

A follow-up for https://github.com/rust-analyzer/rust-analyzer/pull/3694

Same name vs string [issue](https://github.com/rust-analyzer/rust-analyzer/pull/3694#discussion_r396986819) persists here, now I'm able to obtain `ast::Name`, but I see no way to convert it into `hir::Name` or vice versa.

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
This commit is contained in:
bors[bot] 2020-04-01 10:41:46 +00:00 committed by GitHub
commit aad0e63d74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 413 additions and 363 deletions

View file

@ -5,8 +5,7 @@ mod completion_context;
mod presentation;
mod complete_dot;
mod complete_record_literal;
mod complete_record_pattern;
mod complete_record;
mod complete_pattern;
mod complete_fn_param;
mod complete_keyword;
@ -89,8 +88,7 @@ pub(crate) fn completions(
complete_path::complete_path(&mut acc, &ctx);
complete_scope::complete_scope(&mut acc, &ctx);
complete_dot::complete_dot(&mut acc, &ctx);
complete_record_literal::complete_record_literal(&mut acc, &ctx);
complete_record_pattern::complete_record_pattern(&mut acc, &ctx);
complete_record::complete_record(&mut acc, &ctx);
complete_pattern::complete_pattern(&mut acc, &ctx);
complete_postfix::complete_postfix(&mut acc, &ctx);
complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);

View file

@ -0,0 +1,411 @@
//! Complete fields in record literals and patterns.
use crate::completion::{CompletionContext, Completions};
use ra_syntax::{ast, ast::NameOwner, SmolStr};
pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
let (ty, variant, already_present_fields) =
match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) {
(None, None) => return None,
(Some(_), Some(_)) => panic!("A record cannot be both a literal and a pattern"),
(Some(record_pat), _) => (
ctx.sema.type_of_pat(&record_pat.clone().into())?,
ctx.sema.resolve_record_pattern(record_pat)?,
pattern_ascribed_fields(record_pat),
),
(_, Some(record_lit)) => (
ctx.sema.type_of_expr(&record_lit.clone().into())?,
ctx.sema.resolve_record_literal(record_lit)?,
literal_ascribed_fields(record_lit),
),
};
for (field, field_ty) in ty.variant_fields(ctx.db, variant).into_iter().filter(|(field, _)| {
// FIXME: already_present_names better be `Vec<hir::Name>`
!already_present_fields.contains(&SmolStr::from(field.name(ctx.db).to_string()))
}) {
acc.add_field(ctx, field, &field_ty);
}
Some(())
}
fn literal_ascribed_fields(record_lit: &ast::RecordLit) -> Vec<SmolStr> {
record_lit
.record_field_list()
.map(|field_list| field_list.fields())
.map(|fields| {
fields
.into_iter()
.filter_map(|field| field.name_ref())
.map(|name_ref| name_ref.text().clone())
.collect()
})
.unwrap_or_default()
}
fn pattern_ascribed_fields(record_pat: &ast::RecordPat) -> Vec<SmolStr> {
record_pat
.record_field_pat_list()
.map(|pat_list| {
pat_list
.record_field_pats()
.filter_map(|fild_pat| fild_pat.name())
.chain(pat_list.bind_pats().filter_map(|bind_pat| bind_pat.name()))
.map(|name| name.text().clone())
.collect()
})
.unwrap_or_default()
}
#[cfg(test)]
mod tests {
mod record_lit_tests {
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
use insta::assert_debug_snapshot;
fn complete(code: &str) -> Vec<CompletionItem> {
do_completion(code, CompletionKind::Reference)
}
#[test]
fn test_record_pattern_field() {
let completions = complete(
r"
struct S { foo: u32 }
fn process(f: S) {
match f {
S { f<|>: 92 } => (),
}
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "foo",
source_range: [117; 118),
delete: [117; 118),
insert: "foo",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_pattern_enum_variant() {
let completions = complete(
r"
enum E {
S { foo: u32, bar: () }
}
fn process(e: E) {
match e {
E::S { <|> } => (),
}
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "bar",
source_range: [161; 161),
delete: [161; 161),
insert: "bar",
kind: Field,
detail: "()",
},
CompletionItem {
label: "foo",
source_range: [161; 161),
delete: [161; 161),
insert: "foo",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_pattern_field_in_simple_macro() {
let completions = complete(
r"
macro_rules! m { ($e:expr) => { $e } }
struct S { foo: u32 }
fn process(f: S) {
m!(match f {
S { f<|>: 92 } => (),
})
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "foo",
source_range: [171; 172),
delete: [171; 172),
insert: "foo",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn only_missing_fields_are_completed_in_destruct_pats() {
let completions = complete(
r"
struct S {
foo1: u32,
foo2: u32,
bar: u32,
baz: u32,
}
fn main() {
let s = S {
foo1: 1,
foo2: 2,
bar: 3,
baz: 4,
};
if let S { foo1, foo2: a, <|> } = s {}
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "bar",
source_range: [372; 372),
delete: [372; 372),
insert: "bar",
kind: Field,
detail: "u32",
},
CompletionItem {
label: "baz",
source_range: [372; 372),
delete: [372; 372),
insert: "baz",
kind: Field,
detail: "u32",
},
]
"###);
}
}
mod record_pat_tests {
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
use insta::assert_debug_snapshot;
fn complete(code: &str) -> Vec<CompletionItem> {
do_completion(code, CompletionKind::Reference)
}
#[test]
fn test_record_literal_deprecated_field() {
let completions = complete(
r"
struct A {
#[deprecated]
the_field: u32,
}
fn foo() {
A { the<|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "the_field",
source_range: [142; 145),
delete: [142; 145),
insert: "the_field",
kind: Field,
detail: "u32",
deprecated: true,
},
]
"###);
}
#[test]
fn test_record_literal_field() {
let completions = complete(
r"
struct A { the_field: u32 }
fn foo() {
A { the<|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "the_field",
source_range: [83; 86),
delete: [83; 86),
insert: "the_field",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_literal_enum_variant() {
let completions = complete(
r"
enum E {
A { a: u32 }
}
fn foo() {
let _ = E::A { <|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "a",
source_range: [119; 119),
delete: [119; 119),
insert: "a",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_literal_two_structs() {
let completions = complete(
r"
struct A { a: u32 }
struct B { b: u32 }
fn foo() {
let _: A = B { <|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "b",
source_range: [119; 119),
delete: [119; 119),
insert: "b",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_literal_generic_struct() {
let completions = complete(
r"
struct A<T> { a: T }
fn foo() {
let _: A<u32> = A { <|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "a",
source_range: [93; 93),
delete: [93; 93),
insert: "a",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_literal_field_in_simple_macro() {
let completions = complete(
r"
macro_rules! m { ($e:expr) => { $e } }
struct A { the_field: u32 }
fn foo() {
m!(A { the<|> })
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "the_field",
source_range: [137; 140),
delete: [137; 140),
insert: "the_field",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn only_missing_fields_are_completed() {
let completions = complete(
r"
struct S {
foo1: u32,
foo2: u32,
bar: u32,
baz: u32,
}
fn main() {
let foo1 = 1;
let s = S {
foo1,
foo2: 5,
<|>
}
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "bar",
source_range: [302; 302),
delete: [302; 302),
insert: "bar",
kind: Field,
detail: "u32",
},
CompletionItem {
label: "baz",
source_range: [302; 302),
delete: [302; 302),
insert: "baz",
kind: Field,
detail: "u32",
},
]
"###);
}
}
}

View file

@ -1,241 +0,0 @@
//! FIXME: write short doc here
use crate::completion::{CompletionContext, Completions};
use ra_syntax::SmolStr;
/// Complete fields in fields literals.
pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) {
let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| {
Some((ctx.sema.type_of_expr(&it.clone().into())?, ctx.sema.resolve_record_literal(it)?))
}) {
Some(it) => it,
_ => return,
};
let already_present_names: Vec<SmolStr> = ctx
.record_lit_syntax
.as_ref()
.and_then(|record_literal| record_literal.record_field_list())
.map(|field_list| field_list.fields())
.map(|fields| {
fields
.into_iter()
.filter_map(|field| field.name_ref())
.map(|name_ref| name_ref.text().clone())
.collect()
})
.unwrap_or_default();
for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
if !already_present_names.contains(&SmolStr::from(field.name(ctx.db).to_string())) {
acc.add_field(ctx, field, &field_ty);
}
}
}
#[cfg(test)]
mod tests {
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
use insta::assert_debug_snapshot;
fn complete(code: &str) -> Vec<CompletionItem> {
do_completion(code, CompletionKind::Reference)
}
#[test]
fn test_record_literal_deprecated_field() {
let completions = complete(
r"
struct A {
#[deprecated]
the_field: u32,
}
fn foo() {
A { the<|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "the_field",
source_range: [142; 145),
delete: [142; 145),
insert: "the_field",
kind: Field,
detail: "u32",
deprecated: true,
},
]
"###);
}
#[test]
fn test_record_literal_field() {
let completions = complete(
r"
struct A { the_field: u32 }
fn foo() {
A { the<|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "the_field",
source_range: [83; 86),
delete: [83; 86),
insert: "the_field",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_literal_enum_variant() {
let completions = complete(
r"
enum E {
A { a: u32 }
}
fn foo() {
let _ = E::A { <|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "a",
source_range: [119; 119),
delete: [119; 119),
insert: "a",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_literal_two_structs() {
let completions = complete(
r"
struct A { a: u32 }
struct B { b: u32 }
fn foo() {
let _: A = B { <|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "b",
source_range: [119; 119),
delete: [119; 119),
insert: "b",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_literal_generic_struct() {
let completions = complete(
r"
struct A<T> { a: T }
fn foo() {
let _: A<u32> = A { <|> }
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "a",
source_range: [93; 93),
delete: [93; 93),
insert: "a",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_literal_field_in_simple_macro() {
let completions = complete(
r"
macro_rules! m { ($e:expr) => { $e } }
struct A { the_field: u32 }
fn foo() {
m!(A { the<|> })
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "the_field",
source_range: [137; 140),
delete: [137; 140),
insert: "the_field",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn only_missing_fields_are_completed() {
let completions = complete(
r"
struct S {
foo1: u32,
foo2: u32,
bar: u32,
baz: u32,
}
fn main() {
let foo1 = 1;
let s = S {
foo1,
foo2: 5,
<|>
}
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "bar",
source_range: [302; 302),
delete: [302; 302),
insert: "bar",
kind: Field,
detail: "u32",
},
CompletionItem {
label: "baz",
source_range: [302; 302),
delete: [302; 302),
insert: "baz",
kind: Field,
detail: "u32",
},
]
"###);
}
}

View file

@ -1,118 +0,0 @@
//! FIXME: write short doc here
use crate::completion::{CompletionContext, Completions};
pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) {
let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| {
Some((ctx.sema.type_of_pat(&it.clone().into())?, ctx.sema.resolve_record_pattern(it)?))
}) {
Some(it) => it,
_ => return,
};
for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
acc.add_field(ctx, field, &field_ty);
}
}
#[cfg(test)]
mod tests {
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
use insta::assert_debug_snapshot;
fn complete(code: &str) -> Vec<CompletionItem> {
do_completion(code, CompletionKind::Reference)
}
#[test]
fn test_record_pattern_field() {
let completions = complete(
r"
struct S { foo: u32 }
fn process(f: S) {
match f {
S { f<|>: 92 } => (),
}
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "foo",
source_range: [117; 118),
delete: [117; 118),
insert: "foo",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_pattern_enum_variant() {
let completions = complete(
r"
enum E {
S { foo: u32, bar: () }
}
fn process(e: E) {
match e {
E::S { <|> } => (),
}
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "bar",
source_range: [161; 161),
delete: [161; 161),
insert: "bar",
kind: Field,
detail: "()",
},
CompletionItem {
label: "foo",
source_range: [161; 161),
delete: [161; 161),
insert: "foo",
kind: Field,
detail: "u32",
},
]
"###);
}
#[test]
fn test_record_pattern_field_in_simple_macro() {
let completions = complete(
r"
macro_rules! m { ($e:expr) => { $e } }
struct S { foo: u32 }
fn process(f: S) {
m!(match f {
S { f<|>: 92 } => (),
})
}
",
);
assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "foo",
source_range: [171; 172),
delete: [171; 172),
insert: "foo",
kind: Field,
detail: "u32",
},
]
"###);
}
}