diff options
author | Aleksey Kladov <[email protected]> | 2020-04-07 16:09:02 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-04-07 17:34:17 +0100 |
commit | 4c29214bba65d23e18875bd060325c489be5a8e4 (patch) | |
tree | caf82fa6db2c2f7f9293dc556445cc8b0eb47f58 | |
parent | 7819d99d6bc617ee8653e9dc2fa4d82072d6c594 (diff) |
Move computation of missing fields into hir
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 31 | ||||
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 28 | ||||
-rw-r--r-- | crates/ra_hir/src/source_analyzer.rs | 90 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/expr.rs | 158 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_record.rs | 59 |
5 files changed, 198 insertions, 168 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index c6f3bdb8e..9baebf643 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -1027,8 +1027,16 @@ impl Type { | |||
1027 | ty: Ty, | 1027 | ty: Ty, |
1028 | ) -> Option<Type> { | 1028 | ) -> Option<Type> { |
1029 | let krate = resolver.krate()?; | 1029 | let krate = resolver.krate()?; |
1030 | Some(Type::new_with_resolver_inner(db, krate, resolver, ty)) | ||
1031 | } | ||
1032 | pub(crate) fn new_with_resolver_inner( | ||
1033 | db: &dyn HirDatabase, | ||
1034 | krate: CrateId, | ||
1035 | resolver: &Resolver, | ||
1036 | ty: Ty, | ||
1037 | ) -> Type { | ||
1030 | let environment = TraitEnvironment::lower(db, &resolver); | 1038 | let environment = TraitEnvironment::lower(db, &resolver); |
1031 | Some(Type { krate, ty: InEnvironment { value: ty, environment } }) | 1039 | Type { krate, ty: InEnvironment { value: ty, environment } } |
1032 | } | 1040 | } |
1033 | 1041 | ||
1034 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { | 1042 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { |
@@ -1152,27 +1160,6 @@ impl Type { | |||
1152 | res | 1160 | res |
1153 | } | 1161 | } |
1154 | 1162 | ||
1155 | pub fn variant_fields( | ||
1156 | &self, | ||
1157 | db: &dyn HirDatabase, | ||
1158 | def: VariantDef, | ||
1159 | ) -> Vec<(StructField, Type)> { | ||
1160 | // FIXME: check that ty and def match | ||
1161 | match &self.ty.value { | ||
1162 | Ty::Apply(a_ty) => { | ||
1163 | let field_types = db.field_types(def.into()); | ||
1164 | def.fields(db) | ||
1165 | .into_iter() | ||
1166 | .map(|it| { | ||
1167 | let ty = field_types[it.id].clone().subst(&a_ty.parameters); | ||
1168 | (it, self.derived(ty)) | ||
1169 | }) | ||
1170 | .collect() | ||
1171 | } | ||
1172 | _ => Vec::new(), | ||
1173 | } | ||
1174 | } | ||
1175 | |||
1176 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { | 1163 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { |
1177 | // There should be no inference vars in types passed here | 1164 | // There should be no inference vars in types passed here |
1178 | // FIXME check that? | 1165 | // FIXME check that? |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 2ad231d36..2707e422d 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -23,7 +23,7 @@ use crate::{ | |||
23 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 23 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
24 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | 24 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, |
25 | AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name, | 25 | AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name, |
26 | Origin, Path, ScopeDef, StructField, Trait, Type, TypeParam, VariantDef, | 26 | Origin, Path, ScopeDef, StructField, Trait, Type, TypeParam, |
27 | }; | 27 | }; |
28 | 28 | ||
29 | #[derive(Debug, Clone, PartialEq, Eq)] | 29 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -187,14 +187,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
187 | self.analyze(field.syntax()).resolve_record_field(self.db, field) | 187 | self.analyze(field.syntax()).resolve_record_field(self.db, field) |
188 | } | 188 | } |
189 | 189 | ||
190 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> { | ||
191 | self.analyze(record_lit.syntax()).resolve_record_literal(self.db, record_lit) | ||
192 | } | ||
193 | |||
194 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<VariantDef> { | ||
195 | self.analyze(record_pat.syntax()).resolve_record_pattern(record_pat) | ||
196 | } | ||
197 | |||
198 | pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { | 190 | pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { |
199 | let sa = self.analyze(macro_call.syntax()); | 191 | let sa = self.analyze(macro_call.syntax()); |
200 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); | 192 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); |
@@ -212,6 +204,24 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
212 | // FIXME: use this instead? | 204 | // FIXME: use this instead? |
213 | // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>; | 205 | // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>; |
214 | 206 | ||
207 | pub fn record_literal_missing_fields( | ||
208 | &self, | ||
209 | literal: &ast::RecordLit, | ||
210 | ) -> Vec<(StructField, Type)> { | ||
211 | self.analyze(literal.syntax()) | ||
212 | .record_literal_missing_fields(self.db, literal) | ||
213 | .unwrap_or_default() | ||
214 | } | ||
215 | |||
216 | pub fn record_pattern_missing_fields( | ||
217 | &self, | ||
218 | pattern: &ast::RecordPat, | ||
219 | ) -> Vec<(StructField, Type)> { | ||
220 | self.analyze(pattern.syntax()) | ||
221 | .record_pattern_missing_fields(self.db, pattern) | ||
222 | .unwrap_or_default() | ||
223 | } | ||
224 | |||
215 | pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { | 225 | pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { |
216 | let src = self.find_file(src.syntax().clone()).with_value(src).cloned(); | 226 | let src = self.find_file(src.syntax().clone()).with_value(src).cloned(); |
217 | T::to_def(self, src) | 227 | T::to_def(self, src) |
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 815ca158c..45631f8fd 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -14,10 +14,13 @@ use hir_def::{ | |||
14 | }, | 14 | }, |
15 | expr::{ExprId, Pat, PatId}, | 15 | expr::{ExprId, Pat, PatId}, |
16 | resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, | 16 | resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, |
17 | AsMacroCall, DefWithBodyId, | 17 | AsMacroCall, DefWithBodyId, LocalStructFieldId, StructFieldId, VariantId, |
18 | }; | 18 | }; |
19 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; | 19 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; |
20 | use hir_ty::InferenceResult; | 20 | use hir_ty::{ |
21 | expr::{record_literal_missing_fields, record_pattern_missing_fields}, | ||
22 | InferenceResult, Substs, Ty, | ||
23 | }; | ||
21 | use ra_syntax::{ | 24 | use ra_syntax::{ |
22 | ast::{self, AstNode}, | 25 | ast::{self, AstNode}, |
23 | SyntaxNode, SyntaxNodePtr, TextUnit, | 26 | SyntaxNode, SyntaxNodePtr, TextUnit, |
@@ -25,8 +28,10 @@ use ra_syntax::{ | |||
25 | 28 | ||
26 | use crate::{ | 29 | use crate::{ |
27 | db::HirDatabase, semantics::PathResolution, Adt, Const, EnumVariant, Function, Local, MacroDef, | 30 | db::HirDatabase, semantics::PathResolution, Adt, Const, EnumVariant, Function, Local, MacroDef, |
28 | ModPath, ModuleDef, Path, PathKind, Static, Struct, Trait, Type, TypeAlias, TypeParam, | 31 | ModPath, ModuleDef, Path, PathKind, Static, Struct, StructField, Trait, Type, TypeAlias, |
32 | TypeParam, | ||
29 | }; | 33 | }; |
34 | use ra_db::CrateId; | ||
30 | 35 | ||
31 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | 36 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of |
32 | /// original source files. It should not be used inside the HIR itself. | 37 | /// original source files. It should not be used inside the HIR itself. |
@@ -164,23 +169,6 @@ impl SourceAnalyzer { | |||
164 | Some((struct_field.into(), local)) | 169 | Some((struct_field.into(), local)) |
165 | } | 170 | } |
166 | 171 | ||
167 | pub(crate) fn resolve_record_literal( | ||
168 | &self, | ||
169 | db: &dyn HirDatabase, | ||
170 | record_lit: &ast::RecordLit, | ||
171 | ) -> Option<crate::VariantDef> { | ||
172 | let expr_id = self.expr_id(db, &record_lit.clone().into())?; | ||
173 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) | ||
174 | } | ||
175 | |||
176 | pub(crate) fn resolve_record_pattern( | ||
177 | &self, | ||
178 | record_pat: &ast::RecordPat, | ||
179 | ) -> Option<crate::VariantDef> { | ||
180 | let pat_id = self.pat_id(&record_pat.clone().into())?; | ||
181 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) | ||
182 | } | ||
183 | |||
184 | pub(crate) fn resolve_macro_call( | 172 | pub(crate) fn resolve_macro_call( |
185 | &self, | 173 | &self, |
186 | db: &dyn HirDatabase, | 174 | db: &dyn HirDatabase, |
@@ -231,6 +219,68 @@ impl SourceAnalyzer { | |||
231 | resolve_hir_path(db, &self.resolver, &hir_path) | 219 | resolve_hir_path(db, &self.resolver, &hir_path) |
232 | } | 220 | } |
233 | 221 | ||
222 | pub(crate) fn record_literal_missing_fields( | ||
223 | &self, | ||
224 | db: &dyn HirDatabase, | ||
225 | literal: &ast::RecordLit, | ||
226 | ) -> Option<Vec<(StructField, Type)>> { | ||
227 | let krate = self.resolver.krate()?; | ||
228 | let body = self.body.as_ref()?; | ||
229 | let infer = self.infer.as_ref()?; | ||
230 | |||
231 | let expr_id = self.expr_id(db, &literal.clone().into())?; | ||
232 | let substs = match &infer.type_of_expr[expr_id] { | ||
233 | Ty::Apply(a_ty) => &a_ty.parameters, | ||
234 | _ => return None, | ||
235 | }; | ||
236 | |||
237 | let (variant, missing_fields, _exhaustive) = | ||
238 | record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; | ||
239 | let res = self.missing_fields(db, krate, substs, variant, missing_fields); | ||
240 | Some(res) | ||
241 | } | ||
242 | |||
243 | pub(crate) fn record_pattern_missing_fields( | ||
244 | &self, | ||
245 | db: &dyn HirDatabase, | ||
246 | pattern: &ast::RecordPat, | ||
247 | ) -> Option<Vec<(StructField, Type)>> { | ||
248 | let krate = self.resolver.krate()?; | ||
249 | let body = self.body.as_ref()?; | ||
250 | let infer = self.infer.as_ref()?; | ||
251 | |||
252 | let pat_id = self.pat_id(&pattern.clone().into())?; | ||
253 | let substs = match &infer.type_of_pat[pat_id] { | ||
254 | Ty::Apply(a_ty) => &a_ty.parameters, | ||
255 | _ => return None, | ||
256 | }; | ||
257 | |||
258 | let (variant, missing_fields) = | ||
259 | record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; | ||
260 | let res = self.missing_fields(db, krate, substs, variant, missing_fields); | ||
261 | Some(res) | ||
262 | } | ||
263 | |||
264 | fn missing_fields( | ||
265 | &self, | ||
266 | db: &dyn HirDatabase, | ||
267 | krate: CrateId, | ||
268 | substs: &Substs, | ||
269 | variant: VariantId, | ||
270 | missing_fields: Vec<LocalStructFieldId>, | ||
271 | ) -> Vec<(StructField, Type)> { | ||
272 | let field_types = db.field_types(variant); | ||
273 | |||
274 | missing_fields | ||
275 | .into_iter() | ||
276 | .map(|local_id| { | ||
277 | let field = StructFieldId { parent: variant, local_id }; | ||
278 | let ty = field_types[local_id].clone().subst(substs); | ||
279 | (field.into(), Type::new_with_resolver_inner(db, krate, &self.resolver, ty)) | ||
280 | }) | ||
281 | .collect() | ||
282 | } | ||
283 | |||
234 | pub(crate) fn expand( | 284 | pub(crate) fn expand( |
235 | &self, | 285 | &self, |
236 | db: &dyn HirDatabase, | 286 | db: &dyn HirDatabase, |
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs index 1e7395b16..b4592fbf5 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/expr.rs | |||
@@ -2,12 +2,8 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{ | 5 | use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; |
6 | path::{path, Path}, | 6 | use hir_expand::diagnostics::DiagnosticSink; |
7 | resolver::HasResolver, | ||
8 | AdtId, FunctionId, | ||
9 | }; | ||
10 | use hir_expand::{diagnostics::DiagnosticSink, name::Name}; | ||
11 | use ra_syntax::ast; | 7 | use ra_syntax::ast; |
12 | use ra_syntax::AstPtr; | 8 | use ra_syntax::AstPtr; |
13 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
@@ -29,7 +25,7 @@ pub use hir_def::{ | |||
29 | ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, | 25 | ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, |
30 | MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp, | 26 | MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp, |
31 | }, | 27 | }, |
32 | VariantId, | 28 | LocalStructFieldId, VariantId, |
33 | }; | 29 | }; |
34 | 30 | ||
35 | pub struct ExprValidator<'a, 'b: 'a> { | 31 | pub struct ExprValidator<'a, 'b: 'a> { |
@@ -50,14 +46,37 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
50 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { | 46 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { |
51 | let body = db.body(self.func.into()); | 47 | let body = db.body(self.func.into()); |
52 | 48 | ||
53 | for e in body.exprs.iter() { | 49 | for (id, expr) in body.exprs.iter() { |
54 | if let (id, Expr::RecordLit { path, fields, spread }) = e { | 50 | if let Some((variant_def, missed_fields, true)) = |
55 | self.validate_record_literal(id, path, fields, *spread, db); | 51 | record_literal_missing_fields(db, &self.infer, id, expr) |
56 | } else if let (id, Expr::Match { expr, arms }) = e { | 52 | { |
53 | // XXX: only look at source_map if we do have missing fields | ||
54 | let (_, source_map) = db.body_with_source_map(self.func.into()); | ||
55 | |||
56 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
57 | if let Some(expr) = source_ptr.value.left() { | ||
58 | let root = source_ptr.file_syntax(db.upcast()); | ||
59 | if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) { | ||
60 | if let Some(field_list) = record_lit.record_field_list() { | ||
61 | let variant_data = variant_data(db.upcast(), variant_def); | ||
62 | let missed_fields = missed_fields | ||
63 | .into_iter() | ||
64 | .map(|idx| variant_data.fields()[idx].name.clone()) | ||
65 | .collect(); | ||
66 | self.sink.push(MissingFields { | ||
67 | file: source_ptr.file_id, | ||
68 | field_list: AstPtr::new(&field_list), | ||
69 | missed_fields, | ||
70 | }) | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | if let Expr::Match { expr, arms } = expr { | ||
57 | self.validate_match(id, *expr, arms, db, self.infer.clone()); | 77 | self.validate_match(id, *expr, arms, db, self.infer.clone()); |
58 | } | 78 | } |
59 | } | 79 | } |
60 | |||
61 | let body_expr = &body[body.body_expr]; | 80 | let body_expr = &body[body.body_expr]; |
62 | if let Expr::Block { tail: Some(t), .. } = body_expr { | 81 | if let Expr::Block { tail: Some(t), .. } = body_expr { |
63 | self.validate_results_in_tail_expr(body.body_expr, *t, db); | 82 | self.validate_results_in_tail_expr(body.body_expr, *t, db); |
@@ -146,61 +165,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
146 | } | 165 | } |
147 | } | 166 | } |
148 | 167 | ||
149 | fn validate_record_literal( | ||
150 | &mut self, | ||
151 | id: ExprId, | ||
152 | _path: &Option<Path>, | ||
153 | fields: &[RecordLitField], | ||
154 | spread: Option<ExprId>, | ||
155 | db: &dyn HirDatabase, | ||
156 | ) { | ||
157 | if spread.is_some() { | ||
158 | return; | ||
159 | }; | ||
160 | let variant_def: VariantId = match self.infer.variant_resolution_for_expr(id) { | ||
161 | Some(VariantId::UnionId(_)) | None => return, | ||
162 | Some(it) => it, | ||
163 | }; | ||
164 | if let VariantId::UnionId(_) = variant_def { | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | let variant_data = variant_data(db.upcast(), variant_def); | ||
169 | |||
170 | let lit_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); | ||
171 | let missed_fields: Vec<Name> = variant_data | ||
172 | .fields() | ||
173 | .iter() | ||
174 | .filter_map(|(_f, d)| { | ||
175 | let name = d.name.clone(); | ||
176 | if lit_fields.contains(&name) { | ||
177 | None | ||
178 | } else { | ||
179 | Some(name) | ||
180 | } | ||
181 | }) | ||
182 | .collect(); | ||
183 | if missed_fields.is_empty() { | ||
184 | return; | ||
185 | } | ||
186 | let (_, source_map) = db.body_with_source_map(self.func.into()); | ||
187 | |||
188 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
189 | if let Some(expr) = source_ptr.value.left() { | ||
190 | let root = source_ptr.file_syntax(db.upcast()); | ||
191 | if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) { | ||
192 | if let Some(field_list) = record_lit.record_field_list() { | ||
193 | self.sink.push(MissingFields { | ||
194 | file: source_ptr.file_id, | ||
195 | field_list: AstPtr::new(&field_list), | ||
196 | missed_fields, | ||
197 | }) | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | |||
204 | fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &dyn HirDatabase) { | 168 | fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &dyn HirDatabase) { |
205 | // the mismatch will be on the whole block currently | 169 | // the mismatch will be on the whole block currently |
206 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { | 170 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { |
@@ -233,3 +197,63 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
233 | } | 197 | } |
234 | } | 198 | } |
235 | } | 199 | } |
200 | |||
201 | pub fn record_literal_missing_fields( | ||
202 | db: &dyn HirDatabase, | ||
203 | infer: &InferenceResult, | ||
204 | id: ExprId, | ||
205 | expr: &Expr, | ||
206 | ) -> Option<(VariantId, Vec<LocalStructFieldId>, /*exhaustive*/ bool)> { | ||
207 | let (fields, exhausitve) = match expr { | ||
208 | Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()), | ||
209 | _ => return None, | ||
210 | }; | ||
211 | |||
212 | let variant_def = infer.variant_resolution_for_expr(id)?; | ||
213 | if let VariantId::UnionId(_) = variant_def { | ||
214 | return None; | ||
215 | } | ||
216 | |||
217 | let variant_data = variant_data(db.upcast(), variant_def); | ||
218 | |||
219 | let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); | ||
220 | let missed_fields: Vec<LocalStructFieldId> = variant_data | ||
221 | .fields() | ||
222 | .iter() | ||
223 | .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) }) | ||
224 | .collect(); | ||
225 | if missed_fields.is_empty() { | ||
226 | return None; | ||
227 | } | ||
228 | Some((variant_def, missed_fields, exhausitve)) | ||
229 | } | ||
230 | |||
231 | pub fn record_pattern_missing_fields( | ||
232 | db: &dyn HirDatabase, | ||
233 | infer: &InferenceResult, | ||
234 | id: PatId, | ||
235 | pat: &Pat, | ||
236 | ) -> Option<(VariantId, Vec<LocalStructFieldId>)> { | ||
237 | let fields = match pat { | ||
238 | Pat::Record { path: _, args } => args, | ||
239 | _ => return None, | ||
240 | }; | ||
241 | |||
242 | let variant_def = infer.variant_resolution_for_pat(id)?; | ||
243 | if let VariantId::UnionId(_) = variant_def { | ||
244 | return None; | ||
245 | } | ||
246 | |||
247 | let variant_data = variant_data(db.upcast(), variant_def); | ||
248 | |||
249 | let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); | ||
250 | let missed_fields: Vec<LocalStructFieldId> = variant_data | ||
251 | .fields() | ||
252 | .iter() | ||
253 | .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) }) | ||
254 | .collect(); | ||
255 | if missed_fields.is_empty() { | ||
256 | return None; | ||
257 | } | ||
258 | Some((variant_def, missed_fields)) | ||
259 | } | ||
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs index 2352ced5f..f46bcee5c 100644 --- a/crates/ra_ide/src/completion/complete_record.rs +++ b/crates/ra_ide/src/completion/complete_record.rs | |||
@@ -1,60 +1,19 @@ | |||
1 | //! Complete fields in record literals and patterns. | 1 | //! Complete fields in record literals and patterns. |
2 | use ra_syntax::{ast, ast::NameOwner, SmolStr}; | ||
3 | |||
4 | use crate::completion::{CompletionContext, Completions}; | 2 | use crate::completion::{CompletionContext, Completions}; |
5 | 3 | ||
6 | pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 4 | pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
7 | let (ty, variant, already_present_fields) = | 5 | let missing_fields = match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) { |
8 | match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) { | 6 | (None, None) => return None, |
9 | (None, None) => return None, | 7 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), |
10 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), | 8 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), |
11 | (Some(record_pat), _) => ( | 9 | (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit), |
12 | ctx.sema.type_of_pat(&record_pat.clone().into())?, | 10 | }; |
13 | ctx.sema.resolve_record_pattern(record_pat)?, | ||
14 | pattern_ascribed_fields(record_pat), | ||
15 | ), | ||
16 | (_, Some(record_lit)) => ( | ||
17 | ctx.sema.type_of_expr(&record_lit.clone().into())?, | ||
18 | ctx.sema.resolve_record_literal(record_lit)?, | ||
19 | literal_ascribed_fields(record_lit), | ||
20 | ), | ||
21 | }; | ||
22 | 11 | ||
23 | for (field, field_ty) in ty.variant_fields(ctx.db, variant).into_iter().filter(|(field, _)| { | 12 | for (field, ty) in missing_fields { |
24 | // FIXME: already_present_names better be `Vec<hir::Name>` | 13 | acc.add_field(ctx, field, &ty) |
25 | !already_present_fields.contains(&SmolStr::from(field.name(ctx.db).to_string())) | ||
26 | }) { | ||
27 | acc.add_field(ctx, field, &field_ty); | ||
28 | } | 14 | } |
29 | Some(()) | ||
30 | } | ||
31 | 15 | ||
32 | fn literal_ascribed_fields(record_lit: &ast::RecordLit) -> Vec<SmolStr> { | 16 | Some(()) |
33 | record_lit | ||
34 | .record_field_list() | ||
35 | .map(|field_list| field_list.fields()) | ||
36 | .map(|fields| { | ||
37 | fields | ||
38 | .into_iter() | ||
39 | .filter_map(|field| field.name_ref()) | ||
40 | .map(|name_ref| name_ref.text().clone()) | ||
41 | .collect() | ||
42 | }) | ||
43 | .unwrap_or_default() | ||
44 | } | ||
45 | |||
46 | fn pattern_ascribed_fields(record_pat: &ast::RecordPat) -> Vec<SmolStr> { | ||
47 | record_pat | ||
48 | .record_field_pat_list() | ||
49 | .map(|pat_list| { | ||
50 | pat_list | ||
51 | .record_field_pats() | ||
52 | .filter_map(|fild_pat| fild_pat.name()) | ||
53 | .chain(pat_list.bind_pats().filter_map(|bind_pat| bind_pat.name())) | ||
54 | .map(|name| name.text().clone()) | ||
55 | .collect() | ||
56 | }) | ||
57 | .unwrap_or_default() | ||
58 | } | 17 | } |
59 | 18 | ||
60 | #[cfg(test)] | 19 | #[cfg(test)] |