mun_hir/expr/validator/
uninitialized_access.rs

1use super::ExprValidator;
2use crate::diagnostics::{DiagnosticSink, PossiblyUninitializedVariable};
3use crate::resolve::{resolver_for_expr, ValueNs};
4use crate::{BinaryOp, Expr, ExprId, PatId, Path, Resolver, Statement};
5use std::collections::HashSet;
6
7#[derive(Copy, Clone, PartialEq, Eq)]
8enum ExprKind {
9    Normal,
10    Place,
11    Both,
12}
13
14impl<'d> ExprValidator<'d> {
15    /// Validates that all binding access has previously been initialized.
16    pub(super) fn validate_uninitialized_access(&self, sink: &mut DiagnosticSink) {
17        let mut initialized_patterns = HashSet::new();
18
19        // Add all parameter patterns to the set of initialized patterns (they must have been
20        // initialized)
21        for (pat, _) in self.body.params.iter() {
22            initialized_patterns.insert(*pat);
23        }
24
25        self.validate_expr_access(
26            sink,
27            &mut initialized_patterns,
28            self.body.body_expr,
29            ExprKind::Normal,
30        );
31    }
32
33    /// Validates that the specified expr does not access unitialized bindings
34    fn validate_expr_access(
35        &self,
36        sink: &mut DiagnosticSink,
37        initialized_patterns: &mut HashSet<PatId>,
38        expr: ExprId,
39        expr_side: ExprKind,
40    ) {
41        let body = self.body.clone();
42        match &body[expr] {
43            Expr::Call { callee, args } => {
44                self.validate_expr_access(sink, initialized_patterns, *callee, expr_side);
45                for arg in args.iter() {
46                    self.validate_expr_access(sink, initialized_patterns, *arg, expr_side);
47                }
48            }
49            Expr::Path(p) => {
50                let resolver = resolver_for_expr(self.db.upcast(), self.body.owner(), expr);
51                self.validate_path_access(
52                    sink,
53                    initialized_patterns,
54                    &resolver,
55                    p,
56                    expr,
57                    expr_side,
58                );
59            }
60            Expr::If {
61                condition,
62                then_branch,
63                else_branch,
64            } => {
65                self.validate_expr_access(sink, initialized_patterns, *condition, ExprKind::Normal);
66                let mut then_branch_initialized_patterns = initialized_patterns.clone();
67                self.validate_expr_access(
68                    sink,
69                    &mut then_branch_initialized_patterns,
70                    *then_branch,
71                    ExprKind::Normal,
72                );
73                if let Some(else_branch) = else_branch {
74                    let mut else_branch_initialized_patterns = initialized_patterns.clone();
75                    self.validate_expr_access(
76                        sink,
77                        &mut else_branch_initialized_patterns,
78                        *else_branch,
79                        ExprKind::Normal,
80                    );
81                    let then_is_never = self.infer[*then_branch].is_never();
82                    let else_is_never = self.infer[*else_branch].is_never();
83                    match (then_is_never, else_is_never) {
84                        (false, false) => {
85                            initialized_patterns.extend(
86                                then_branch_initialized_patterns
87                                    .intersection(&else_branch_initialized_patterns),
88                            );
89                        }
90                        (true, false) => {
91                            initialized_patterns.extend(else_branch_initialized_patterns);
92                        }
93                        (false, true) => {
94                            initialized_patterns.extend(then_branch_initialized_patterns);
95                        }
96                        (true, true) => {}
97                    };
98                }
99            }
100            Expr::UnaryOp { expr, .. } => {
101                self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal);
102            }
103            Expr::BinaryOp { lhs, rhs, op } => {
104                let lhs_expr_kind = match op {
105                    Some(BinaryOp::Assignment { op: Some(_) }) => ExprKind::Both,
106                    Some(BinaryOp::Assignment { op: None }) => ExprKind::Place,
107                    _ => ExprKind::Normal,
108                };
109                self.validate_expr_access(sink, initialized_patterns, *lhs, lhs_expr_kind);
110                self.validate_expr_access(sink, initialized_patterns, *rhs, ExprKind::Normal)
111            }
112            Expr::Block { statements, tail } => {
113                for statement in statements.iter() {
114                    match statement {
115                        Statement::Let {
116                            pat, initializer, ..
117                        } => {
118                            if let Some(initializer) = initializer {
119                                self.validate_expr_access(
120                                    sink,
121                                    initialized_patterns,
122                                    *initializer,
123                                    ExprKind::Normal,
124                                );
125                                initialized_patterns.insert(*pat);
126                            }
127                        }
128                        Statement::Expr(expr) => {
129                            self.validate_expr_access(
130                                sink,
131                                initialized_patterns,
132                                *expr,
133                                ExprKind::Normal,
134                            );
135                            if self.infer[*expr].is_never() {
136                                return;
137                            }
138                        }
139                    }
140                }
141                if let Some(tail) = tail {
142                    self.validate_expr_access(sink, initialized_patterns, *tail, ExprKind::Normal)
143                }
144            }
145            Expr::Return { expr } => {
146                if let Some(expr) = expr {
147                    self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal)
148                }
149            }
150            Expr::Break { expr } => {
151                if let Some(expr) = expr {
152                    self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal)
153                }
154            }
155            Expr::Loop { body } => {
156                self.validate_expr_access(sink, initialized_patterns, *body, ExprKind::Normal)
157            }
158            Expr::While { condition, body } => {
159                self.validate_expr_access(sink, initialized_patterns, *condition, ExprKind::Normal);
160                self.validate_expr_access(
161                    sink,
162                    &mut initialized_patterns.clone(),
163                    *body,
164                    ExprKind::Normal,
165                );
166            }
167            Expr::RecordLit { fields, spread, .. } => {
168                for field in fields.iter() {
169                    self.validate_expr_access(
170                        sink,
171                        initialized_patterns,
172                        field.expr,
173                        ExprKind::Normal,
174                    );
175                }
176                if let Some(expr) = spread {
177                    self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal);
178                }
179            }
180            Expr::Field { expr, .. } => {
181                self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal);
182            }
183            Expr::Index { base, index } => {
184                self.validate_expr_access(sink, initialized_patterns, *base, ExprKind::Normal);
185                self.validate_expr_access(sink, initialized_patterns, *index, ExprKind::Normal);
186            }
187            Expr::Array(exprs) => {
188                for expr in exprs {
189                    self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal);
190                }
191            }
192            Expr::Literal(_) => {}
193            Expr::Missing => {}
194        }
195    }
196
197    fn validate_path_access(
198        &self,
199        sink: &mut DiagnosticSink,
200        initialized_patterns: &mut HashSet<PatId>,
201        resolver: &Resolver,
202        path: &Path,
203        expr: ExprId,
204        expr_side: ExprKind,
205    ) {
206        let pat = match resolver.resolve_path_as_value_fully(self.db.upcast(), path) {
207            Some((ValueNs::LocalBinding(pat), _)) => pat,
208            _ => return,
209        };
210
211        if expr_side == ExprKind::Normal || expr_side == ExprKind::Both {
212            // Check if the binding has already been initialized
213            if initialized_patterns.get(&pat).is_none() {
214                let (_, body_source_map) = self.db.body_with_source_map(self.func.id.into());
215                sink.push(PossiblyUninitializedVariable {
216                    file: self.func.file_id(self.db),
217                    pat: body_source_map
218                        .expr_syntax(expr)
219                        .unwrap()
220                        .value
221                        .either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()),
222                })
223            }
224        }
225
226        if expr_side == ExprKind::Place {
227            // The binding should be initialized
228            initialized_patterns.insert(pat);
229        }
230    }
231}