From 2a1e11b36fe90460b139f2f6aee034f63e8252bf Mon Sep 17 00:00:00 2001 From: Ekaterina Babshukova Date: Fri, 12 Jul 2019 19:56:18 +0300 Subject: complete fields in enum variants --- crates/ra_hir/src/adt.rs | 7 ++ crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/source_binder.rs | 5 + crates/ra_hir/src/ty/infer.rs | 15 +++ .../src/completion/complete_struct_literal.rs | 103 ++++++++++++++++++--- 5 files changed, 116 insertions(+), 16 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index 5a3ea5f55..8afdac801 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -185,6 +185,13 @@ pub enum VariantDef { impl_froms!(VariantDef: Struct, EnumVariant); impl VariantDef { + pub fn fields(self, db: &impl HirDatabase) -> Vec { + match self { + VariantDef::Struct(it) => it.fields(db), + VariantDef::EnumVariant(it) => it.fields(db), + } + } + pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option { match self { VariantDef::Struct(it) => it.field(db, name), diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 56831ba05..55d1298cf 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -55,7 +55,7 @@ use crate::{ }; pub use self::{ - adt::AdtDef, + adt::{AdtDef, VariantDef}, either::Either, expr::ExprScopes, generics::{GenericParam, GenericParams, HasGenericParams}, diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 573add7da..071c1bb18 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -266,6 +266,11 @@ impl SourceAnalyzer { self.infer.as_ref()?.field_resolution(expr_id) } + pub fn resolve_variant(&self, struct_lit: &ast::StructLit) -> Option { + let expr_id = self.body_source_map.as_ref()?.node_expr(struct_lit.into())?; + self.infer.as_ref()?.variant_resolution(expr_id) + } + pub fn resolve_macro_call( &self, db: &impl HirDatabase, diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 26ddf0317..5ad4f73ec 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -113,6 +113,7 @@ pub struct InferenceResult { method_resolutions: FxHashMap, /// For each field access expr, records the field it resolves to. field_resolutions: FxHashMap, + variant_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, diagnostics: Vec, @@ -127,6 +128,9 @@ impl InferenceResult { pub fn field_resolution(&self, expr: ExprId) -> Option { self.field_resolutions.get(&expr).copied() } + pub fn variant_resolution(&self, expr: ExprId) -> Option { + self.variant_resolutions.get(&expr).copied() + } pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option { self.assoc_resolutions.get(&id.into()).copied() } @@ -170,6 +174,7 @@ struct InferenceContext<'a, D: HirDatabase> { obligations: Vec, method_resolutions: FxHashMap, field_resolutions: FxHashMap, + variant_resolutions: FxHashMap, assoc_resolutions: FxHashMap, type_of_expr: ArenaMap, type_of_pat: ArenaMap, @@ -183,6 +188,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { InferenceContext { method_resolutions: FxHashMap::default(), field_resolutions: FxHashMap::default(), + variant_resolutions: FxHashMap::default(), assoc_resolutions: FxHashMap::default(), type_of_expr: ArenaMap::default(), type_of_pat: ArenaMap::default(), @@ -213,6 +219,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { InferenceResult { method_resolutions: self.method_resolutions, field_resolutions: self.field_resolutions, + variant_resolutions: self.variant_resolutions, assoc_resolutions: self.assoc_resolutions, type_of_expr: expr_types, type_of_pat: pat_types, @@ -232,6 +239,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.field_resolutions.insert(expr, field); } + fn write_variant_resolution(&mut self, expr: ExprId, variant: VariantDef) { + self.variant_resolutions.insert(expr, variant); + } + fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: ImplItem) { self.assoc_resolutions.insert(id, item); } @@ -1069,6 +1080,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } Expr::StructLit { path, fields, spread } => { let (ty, def_id) = self.resolve_variant(path.as_ref()); + if let Some(variant) = def_id { + self.write_variant_resolution(tgt_expr, variant); + } + let substs = ty.substs().unwrap_or_else(Substs::empty); for (field_idx, field) in fields.iter().enumerate() { let field_ty = def_id diff --git a/crates/ra_ide_api/src/completion/complete_struct_literal.rs b/crates/ra_ide_api/src/completion/complete_struct_literal.rs index 35fb21113..b6216f857 100644 --- a/crates/ra_ide_api/src/completion/complete_struct_literal.rs +++ b/crates/ra_ide_api/src/completion/complete_struct_literal.rs @@ -1,28 +1,24 @@ -use hir::AdtDef; +use hir::{Substs, Ty}; use crate::completion::{CompletionContext, Completions}; /// Complete fields in fields literals. pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { - let ty = match ctx.struct_lit_syntax.and_then(|it| ctx.analyzer.type_of(ctx.db, it.into())) { + let (ty, variant) = match ctx.struct_lit_syntax.and_then(|it| { + Some((ctx.analyzer.type_of(ctx.db, it.into())?, ctx.analyzer.resolve_variant(it)?)) + }) { Some(it) => it, - None => return, - }; - let (adt, substs) = match ty.as_adt() { - Some(res) => res, _ => return, }; - match adt { - AdtDef::Struct(s) => { - for field in s.fields(ctx.db) { - acc.add_field(ctx, field, substs); - } - } - // FIXME unions - AdtDef::Union(_) => (), - AdtDef::Enum(_) => (), + let ty_substs = match ty { + Ty::Apply(it) => it.parameters, + _ => Substs::empty(), }; + + for field in variant.fields(ctx.db) { + acc.add_field(ctx, field, &ty_substs); + } } #[cfg(test)] @@ -57,4 +53,81 @@ mod tests { ⋮] "###); } + + #[test] + fn test_struct_literal_enum_variant() { + let completions = complete( + r" + enum E { + A { a: u32 } + } + fn foo() { + let _ = E::A { <|> } + } + ", + ); + assert_debug_snapshot_matches!(completions, @r###" + ⋮[ + ⋮ CompletionItem { + ⋮ label: "a", + ⋮ source_range: [119; 119), + ⋮ delete: [119; 119), + ⋮ insert: "a", + ⋮ kind: Field, + ⋮ detail: "u32", + ⋮ }, + ⋮] + "###); + } + + #[test] + fn test_struct_literal_two_structs() { + let completions = complete( + r" + struct A { a: u32 } + struct B { b: u32 } + + fn foo() { + let _: A = B { <|> } + } + ", + ); + assert_debug_snapshot_matches!(completions, @r###" + ⋮[ + ⋮ CompletionItem { + ⋮ label: "b", + ⋮ source_range: [119; 119), + ⋮ delete: [119; 119), + ⋮ insert: "b", + ⋮ kind: Field, + ⋮ detail: "u32", + ⋮ }, + ⋮] + "###); + } + + #[test] + fn test_struct_literal_generic_struct() { + let completions = complete( + r" + struct A { a: T } + + fn foo() { + let _: A = A { <|> } + } + ", + ); + assert_debug_snapshot_matches!(completions, @r###" + ⋮[ + ⋮ CompletionItem { + ⋮ label: "a", + ⋮ source_range: [93; 93), + ⋮ delete: [93; 93), + ⋮ insert: "a", + ⋮ kind: Field, + ⋮ detail: "u32", + ⋮ }, + ⋮] + "###); + } } -- cgit v1.2.3