diff options
Diffstat (limited to 'crates/ra_hir/src/expr')
-rw-r--r-- | crates/ra_hir/src/expr/validation.rs | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs new file mode 100644 index 000000000..fd4907313 --- /dev/null +++ b/crates/ra_hir/src/expr/validation.rs | |||
@@ -0,0 +1,92 @@ | |||
1 | use std::sync::Arc; | ||
2 | use rustc_hash::FxHashSet; | ||
3 | |||
4 | use ra_syntax::ast::{AstNode, StructLit}; | ||
5 | |||
6 | use crate::{ | ||
7 | expr::AstPtr, | ||
8 | HirDatabase, | ||
9 | Function, | ||
10 | Name, | ||
11 | diagnostics::{DiagnosticSink, MissingFields}, | ||
12 | adt::AdtDef, | ||
13 | Path, | ||
14 | ty::InferenceResult | ||
15 | }; | ||
16 | use super::{Expr, StructLitField, ExprId}; | ||
17 | |||
18 | pub(crate) struct ExprValidator<'a, 'b: 'a> { | ||
19 | func: Function, | ||
20 | infer: Arc<InferenceResult>, | ||
21 | sink: &'a mut DiagnosticSink<'b>, | ||
22 | } | ||
23 | |||
24 | impl<'a, 'b> ExprValidator<'a, 'b> { | ||
25 | pub(crate) fn new( | ||
26 | func: Function, | ||
27 | infer: Arc<InferenceResult>, | ||
28 | sink: &'a mut DiagnosticSink<'b>, | ||
29 | ) -> ExprValidator<'a, 'b> { | ||
30 | ExprValidator { func, infer, sink } | ||
31 | } | ||
32 | |||
33 | pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) { | ||
34 | let body = self.func.body(db); | ||
35 | for e in body.exprs() { | ||
36 | match e { | ||
37 | (id, Expr::StructLit { path, fields, spread }) => { | ||
38 | self.validate_struct_literal(id, path, fields, spread, db) | ||
39 | } | ||
40 | _ => (), | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | fn validate_struct_literal( | ||
46 | &mut self, | ||
47 | id: ExprId, | ||
48 | _path: &Option<Path>, | ||
49 | fields: &Vec<StructLitField>, | ||
50 | spread: &Option<ExprId>, | ||
51 | db: &impl HirDatabase, | ||
52 | ) { | ||
53 | if let Some(_) = spread { | ||
54 | return; | ||
55 | } | ||
56 | let lit_fields: FxHashSet<_> = fields.into_iter().map(|f| &f.name).collect(); | ||
57 | let struct_ty = &self.infer[id]; | ||
58 | if let Some((AdtDef::Struct(s), _)) = struct_ty.as_adt() { | ||
59 | let missed_fields: Vec<Name> = s | ||
60 | .fields(db) | ||
61 | .iter() | ||
62 | .filter_map(|f| { | ||
63 | let name = f.name(db); | ||
64 | if lit_fields.contains(&name) { | ||
65 | None | ||
66 | } else { | ||
67 | Some(name) | ||
68 | } | ||
69 | }) | ||
70 | .collect(); | ||
71 | if missed_fields.is_empty() { | ||
72 | return; | ||
73 | } | ||
74 | let source_map = self.func.body_source_map(db); | ||
75 | let file_id = self.func.source(db).0; | ||
76 | let source_file = db.parse(file_id.original_file(db)); | ||
77 | if let Some(field_list_node) = source_map | ||
78 | .expr_syntax(id) | ||
79 | .map(|ptr| ptr.to_node(&source_file)) | ||
80 | .and_then(StructLit::cast) | ||
81 | .and_then(|lit| lit.named_field_list()) | ||
82 | { | ||
83 | let field_list_ptr = AstPtr::new(field_list_node); | ||
84 | self.sink.push(MissingFields { | ||
85 | file: file_id, | ||
86 | field_list: field_list_ptr, | ||
87 | missed_fields, | ||
88 | }) | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | } | ||