aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-04-07 16:09:02 +0100
committerAleksey Kladov <[email protected]>2020-04-07 17:34:17 +0100
commit4c29214bba65d23e18875bd060325c489be5a8e4 (patch)
treecaf82fa6db2c2f7f9293dc556445cc8b0eb47f58 /crates/ra_hir_ty
parent7819d99d6bc617ee8653e9dc2fa4d82072d6c594 (diff)
Move computation of missing fields into hir
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/expr.rs158
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
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{ 5use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId};
6 path::{path, Path}, 6use hir_expand::diagnostics::DiagnosticSink;
7 resolver::HasResolver,
8 AdtId, FunctionId,
9};
10use hir_expand::{diagnostics::DiagnosticSink, name::Name};
11use ra_syntax::ast; 7use ra_syntax::ast;
12use ra_syntax::AstPtr; 8use ra_syntax::AstPtr;
13use rustc_hash::FxHashSet; 9use 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
35pub struct ExprValidator<'a, 'b: 'a> { 31pub 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
201pub 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
231pub 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}