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 pub(super) fn validate_uninitialized_access(&self, sink: &mut DiagnosticSink) {
17 let mut initialized_patterns = HashSet::new();
18
19 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 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 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 initialized_patterns.insert(pat);
229 }
230 }
231}