diff options
author | Ekaterina Babshukova <[email protected]> | 2019-07-12 17:56:18 +0100 |
---|---|---|
committer | Ekaterina Babshukova <[email protected]> | 2019-07-12 18:31:49 +0100 |
commit | 2a1e11b36fe90460b139f2f6aee034f63e8252bf (patch) | |
tree | 8275d86d2d105a2bd2a5d601af9b6372a523107d | |
parent | 8bb81d7418dbc4c295d31d261441b67dba4c0f76 (diff) |
complete fields in enum variants
-rw-r--r-- | crates/ra_hir/src/adt.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 15 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_struct_literal.rs | 103 |
5 files changed, 116 insertions, 16 deletions
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 { | |||
185 | impl_froms!(VariantDef: Struct, EnumVariant); | 185 | impl_froms!(VariantDef: Struct, EnumVariant); |
186 | 186 | ||
187 | impl VariantDef { | 187 | impl VariantDef { |
188 | pub fn fields(self, db: &impl HirDatabase) -> Vec<StructField> { | ||
189 | match self { | ||
190 | VariantDef::Struct(it) => it.fields(db), | ||
191 | VariantDef::EnumVariant(it) => it.fields(db), | ||
192 | } | ||
193 | } | ||
194 | |||
188 | pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> { | 195 | pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> { |
189 | match self { | 196 | match self { |
190 | VariantDef::Struct(it) => it.field(db, name), | 197 | 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::{ | |||
55 | }; | 55 | }; |
56 | 56 | ||
57 | pub use self::{ | 57 | pub use self::{ |
58 | adt::AdtDef, | 58 | adt::{AdtDef, VariantDef}, |
59 | either::Either, | 59 | either::Either, |
60 | expr::ExprScopes, | 60 | expr::ExprScopes, |
61 | generics::{GenericParam, GenericParams, HasGenericParams}, | 61 | 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 { | |||
266 | self.infer.as_ref()?.field_resolution(expr_id) | 266 | self.infer.as_ref()?.field_resolution(expr_id) |
267 | } | 267 | } |
268 | 268 | ||
269 | pub fn resolve_variant(&self, struct_lit: &ast::StructLit) -> Option<crate::VariantDef> { | ||
270 | let expr_id = self.body_source_map.as_ref()?.node_expr(struct_lit.into())?; | ||
271 | self.infer.as_ref()?.variant_resolution(expr_id) | ||
272 | } | ||
273 | |||
269 | pub fn resolve_macro_call( | 274 | pub fn resolve_macro_call( |
270 | &self, | 275 | &self, |
271 | db: &impl HirDatabase, | 276 | 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 { | |||
113 | method_resolutions: FxHashMap<ExprId, Function>, | 113 | method_resolutions: FxHashMap<ExprId, Function>, |
114 | /// For each field access expr, records the field it resolves to. | 114 | /// For each field access expr, records the field it resolves to. |
115 | field_resolutions: FxHashMap<ExprId, StructField>, | 115 | field_resolutions: FxHashMap<ExprId, StructField>, |
116 | variant_resolutions: FxHashMap<ExprId, VariantDef>, | ||
116 | /// For each associated item record what it resolves to | 117 | /// For each associated item record what it resolves to |
117 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 118 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
118 | diagnostics: Vec<InferenceDiagnostic>, | 119 | diagnostics: Vec<InferenceDiagnostic>, |
@@ -127,6 +128,9 @@ impl InferenceResult { | |||
127 | pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> { | 128 | pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> { |
128 | self.field_resolutions.get(&expr).copied() | 129 | self.field_resolutions.get(&expr).copied() |
129 | } | 130 | } |
131 | pub fn variant_resolution(&self, expr: ExprId) -> Option<VariantDef> { | ||
132 | self.variant_resolutions.get(&expr).copied() | ||
133 | } | ||
130 | pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<ImplItem> { | 134 | pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<ImplItem> { |
131 | self.assoc_resolutions.get(&id.into()).copied() | 135 | self.assoc_resolutions.get(&id.into()).copied() |
132 | } | 136 | } |
@@ -170,6 +174,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
170 | obligations: Vec<Obligation>, | 174 | obligations: Vec<Obligation>, |
171 | method_resolutions: FxHashMap<ExprId, Function>, | 175 | method_resolutions: FxHashMap<ExprId, Function>, |
172 | field_resolutions: FxHashMap<ExprId, StructField>, | 176 | field_resolutions: FxHashMap<ExprId, StructField>, |
177 | variant_resolutions: FxHashMap<ExprId, VariantDef>, | ||
173 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 178 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
174 | type_of_expr: ArenaMap<ExprId, Ty>, | 179 | type_of_expr: ArenaMap<ExprId, Ty>, |
175 | type_of_pat: ArenaMap<PatId, Ty>, | 180 | type_of_pat: ArenaMap<PatId, Ty>, |
@@ -183,6 +188,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
183 | InferenceContext { | 188 | InferenceContext { |
184 | method_resolutions: FxHashMap::default(), | 189 | method_resolutions: FxHashMap::default(), |
185 | field_resolutions: FxHashMap::default(), | 190 | field_resolutions: FxHashMap::default(), |
191 | variant_resolutions: FxHashMap::default(), | ||
186 | assoc_resolutions: FxHashMap::default(), | 192 | assoc_resolutions: FxHashMap::default(), |
187 | type_of_expr: ArenaMap::default(), | 193 | type_of_expr: ArenaMap::default(), |
188 | type_of_pat: ArenaMap::default(), | 194 | type_of_pat: ArenaMap::default(), |
@@ -213,6 +219,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
213 | InferenceResult { | 219 | InferenceResult { |
214 | method_resolutions: self.method_resolutions, | 220 | method_resolutions: self.method_resolutions, |
215 | field_resolutions: self.field_resolutions, | 221 | field_resolutions: self.field_resolutions, |
222 | variant_resolutions: self.variant_resolutions, | ||
216 | assoc_resolutions: self.assoc_resolutions, | 223 | assoc_resolutions: self.assoc_resolutions, |
217 | type_of_expr: expr_types, | 224 | type_of_expr: expr_types, |
218 | type_of_pat: pat_types, | 225 | type_of_pat: pat_types, |
@@ -232,6 +239,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
232 | self.field_resolutions.insert(expr, field); | 239 | self.field_resolutions.insert(expr, field); |
233 | } | 240 | } |
234 | 241 | ||
242 | fn write_variant_resolution(&mut self, expr: ExprId, variant: VariantDef) { | ||
243 | self.variant_resolutions.insert(expr, variant); | ||
244 | } | ||
245 | |||
235 | fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: ImplItem) { | 246 | fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: ImplItem) { |
236 | self.assoc_resolutions.insert(id, item); | 247 | self.assoc_resolutions.insert(id, item); |
237 | } | 248 | } |
@@ -1069,6 +1080,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1069 | } | 1080 | } |
1070 | Expr::StructLit { path, fields, spread } => { | 1081 | Expr::StructLit { path, fields, spread } => { |
1071 | let (ty, def_id) = self.resolve_variant(path.as_ref()); | 1082 | let (ty, def_id) = self.resolve_variant(path.as_ref()); |
1083 | if let Some(variant) = def_id { | ||
1084 | self.write_variant_resolution(tgt_expr, variant); | ||
1085 | } | ||
1086 | |||
1072 | let substs = ty.substs().unwrap_or_else(Substs::empty); | 1087 | let substs = ty.substs().unwrap_or_else(Substs::empty); |
1073 | for (field_idx, field) in fields.iter().enumerate() { | 1088 | for (field_idx, field) in fields.iter().enumerate() { |
1074 | let field_ty = def_id | 1089 | 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 @@ | |||
1 | use hir::AdtDef; | 1 | use hir::{Substs, Ty}; |
2 | 2 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
5 | /// Complete fields in fields literals. | 5 | /// Complete fields in fields literals. |
6 | pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { |
7 | let ty = match ctx.struct_lit_syntax.and_then(|it| ctx.analyzer.type_of(ctx.db, it.into())) { | 7 | let (ty, variant) = match ctx.struct_lit_syntax.and_then(|it| { |
8 | Some((ctx.analyzer.type_of(ctx.db, it.into())?, ctx.analyzer.resolve_variant(it)?)) | ||
9 | }) { | ||
8 | Some(it) => it, | 10 | Some(it) => it, |
9 | None => return, | ||
10 | }; | ||
11 | let (adt, substs) = match ty.as_adt() { | ||
12 | Some(res) => res, | ||
13 | _ => return, | 11 | _ => return, |
14 | }; | 12 | }; |
15 | match adt { | ||
16 | AdtDef::Struct(s) => { | ||
17 | for field in s.fields(ctx.db) { | ||
18 | acc.add_field(ctx, field, substs); | ||
19 | } | ||
20 | } | ||
21 | 13 | ||
22 | // FIXME unions | 14 | let ty_substs = match ty { |
23 | AdtDef::Union(_) => (), | 15 | Ty::Apply(it) => it.parameters, |
24 | AdtDef::Enum(_) => (), | 16 | _ => Substs::empty(), |
25 | }; | 17 | }; |
18 | |||
19 | for field in variant.fields(ctx.db) { | ||
20 | acc.add_field(ctx, field, &ty_substs); | ||
21 | } | ||
26 | } | 22 | } |
27 | 23 | ||
28 | #[cfg(test)] | 24 | #[cfg(test)] |
@@ -57,4 +53,81 @@ mod tests { | |||
57 | ⋮] | 53 | ⋮] |
58 | "###); | 54 | "###); |
59 | } | 55 | } |
56 | |||
57 | #[test] | ||
58 | fn test_struct_literal_enum_variant() { | ||
59 | let completions = complete( | ||
60 | r" | ||
61 | enum E { | ||
62 | A { a: u32 } | ||
63 | } | ||
64 | fn foo() { | ||
65 | let _ = E::A { <|> } | ||
66 | } | ||
67 | ", | ||
68 | ); | ||
69 | assert_debug_snapshot_matches!(completions, @r###" | ||
70 | ⋮[ | ||
71 | ⋮ CompletionItem { | ||
72 | ⋮ label: "a", | ||
73 | ⋮ source_range: [119; 119), | ||
74 | ⋮ delete: [119; 119), | ||
75 | ⋮ insert: "a", | ||
76 | ⋮ kind: Field, | ||
77 | ⋮ detail: "u32", | ||
78 | ⋮ }, | ||
79 | ⋮] | ||
80 | "###); | ||
81 | } | ||
82 | |||
83 | #[test] | ||
84 | fn test_struct_literal_two_structs() { | ||
85 | let completions = complete( | ||
86 | r" | ||
87 | struct A { a: u32 } | ||
88 | struct B { b: u32 } | ||
89 | |||
90 | fn foo() { | ||
91 | let _: A = B { <|> } | ||
92 | } | ||
93 | ", | ||
94 | ); | ||
95 | assert_debug_snapshot_matches!(completions, @r###" | ||
96 | ⋮[ | ||
97 | ⋮ CompletionItem { | ||
98 | ⋮ label: "b", | ||
99 | ⋮ source_range: [119; 119), | ||
100 | ⋮ delete: [119; 119), | ||
101 | ⋮ insert: "b", | ||
102 | ⋮ kind: Field, | ||
103 | ⋮ detail: "u32", | ||
104 | ⋮ }, | ||
105 | ⋮] | ||
106 | "###); | ||
107 | } | ||
108 | |||
109 | #[test] | ||
110 | fn test_struct_literal_generic_struct() { | ||
111 | let completions = complete( | ||
112 | r" | ||
113 | struct A<T> { a: T } | ||
114 | |||
115 | fn foo() { | ||
116 | let _: A<u32> = A { <|> } | ||
117 | } | ||
118 | ", | ||
119 | ); | ||
120 | assert_debug_snapshot_matches!(completions, @r###" | ||
121 | ⋮[ | ||
122 | ⋮ CompletionItem { | ||
123 | ⋮ label: "a", | ||
124 | ⋮ source_range: [93; 93), | ||
125 | ⋮ delete: [93; 93), | ||
126 | ⋮ insert: "a", | ||
127 | ⋮ kind: Field, | ||
128 | ⋮ detail: "u32", | ||
129 | ⋮ }, | ||
130 | ⋮] | ||
131 | "###); | ||
132 | } | ||
60 | } | 133 | } |