diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-04-07 17:48:15 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-04-07 17:48:15 +0100 |
commit | 0c927b458421c488323efbf742ce7017b6da29ab (patch) | |
tree | caf82fa6db2c2f7f9293dc556445cc8b0eb47f58 /crates/ra_hir_ty | |
parent | 173dccc804e75da1c83532bc0dbb21f074777731 (diff) | |
parent | 4c29214bba65d23e18875bd060325c489be5a8e4 (diff) |
Merge #3882
3882: Move computation of missing fields into hir r=matklad a=matklad
cc @SomeoneToIgnore, this is that refactoring that moves computation of missing fields to hir.
it actually removes meaningful duplication between diagnostics code and the completion code. Nontheless, it's a net addition of code :(
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r-- | crates/ra_hir_ty/src/expr.rs | 158 |
1 files changed, 91 insertions, 67 deletions
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 | } | ||