aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/expr/validation.rs
blob: fd49073136bf34b00af1a0cc47f5f2b49b0f252c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use std::sync::Arc;
use rustc_hash::FxHashSet;

use ra_syntax::ast::{AstNode, StructLit};

use crate::{
    expr::AstPtr,
    HirDatabase,
    Function,
    Name,
    diagnostics::{DiagnosticSink, MissingFields},
    adt::AdtDef,
    Path,
    ty::InferenceResult
};
use super::{Expr, StructLitField, ExprId};

pub(crate) struct ExprValidator<'a, 'b: 'a> {
    func: Function,
    infer: Arc<InferenceResult>,
    sink: &'a mut DiagnosticSink<'b>,
}

impl<'a, 'b> ExprValidator<'a, 'b> {
    pub(crate) fn new(
        func: Function,
        infer: Arc<InferenceResult>,
        sink: &'a mut DiagnosticSink<'b>,
    ) -> ExprValidator<'a, 'b> {
        ExprValidator { func, infer, sink }
    }

    pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) {
        let body = self.func.body(db);
        for e in body.exprs() {
            match e {
                (id, Expr::StructLit { path, fields, spread }) => {
                    self.validate_struct_literal(id, path, fields, spread, db)
                }
                _ => (),
            }
        }
    }

    fn validate_struct_literal(
        &mut self,
        id: ExprId,
        _path: &Option<Path>,
        fields: &Vec<StructLitField>,
        spread: &Option<ExprId>,
        db: &impl HirDatabase,
    ) {
        if let Some(_) = spread {
            return;
        }
        let lit_fields: FxHashSet<_> = fields.into_iter().map(|f| &f.name).collect();
        let struct_ty = &self.infer[id];
        if let Some((AdtDef::Struct(s), _)) = struct_ty.as_adt() {
            let missed_fields: Vec<Name> = s
                .fields(db)
                .iter()
                .filter_map(|f| {
                    let name = f.name(db);
                    if lit_fields.contains(&name) {
                        None
                    } else {
                        Some(name)
                    }
                })
                .collect();
            if missed_fields.is_empty() {
                return;
            }
            let source_map = self.func.body_source_map(db);
            let file_id = self.func.source(db).0;
            let source_file = db.parse(file_id.original_file(db));
            if let Some(field_list_node) = source_map
                .expr_syntax(id)
                .map(|ptr| ptr.to_node(&source_file))
                .and_then(StructLit::cast)
                .and_then(|lit| lit.named_field_list())
            {
                let field_list_ptr = AstPtr::new(field_list_node);
                self.sink.push(MissingFields {
                    file: file_id,
                    field_list: field_list_ptr,
                    missed_fields,
                })
            }
        }
    }
}