1use std::collections::{HashMap, HashSet};
20
21use crate::ast::{
22 BlockItem, BuiltinArg, CompoundStmt, Declaration, DerivedDecl, Expr, ExprKind, ForInit,
23 FunctionDef, Initializer, InitializerItem, Stmt,
24};
25use crate::intern::InternedStr;
26use crate::macro_infer::{MacroParam, ParseResult};
27
28#[derive(Debug, Default, Clone)]
30pub struct NameUsage {
31 pub used: bool,
33 pub needs_mut: bool,
35 pub addr_taken_before_assign: bool,
39 assigned_unconditionally: bool,
42}
43
44#[derive(Debug, Default, Clone)]
46pub struct LocalUsageAnalysis {
47 names: HashMap<InternedStr, NameUsage>,
48}
49
50impl LocalUsageAnalysis {
51 pub fn needs_mut(&self, name: InternedStr) -> bool {
53 self.names.get(&name).is_some_and(|u| u.needs_mut)
54 }
55
56 pub fn is_unused(&self, name: InternedStr) -> bool {
58 self.names.get(&name).is_some_and(|u| !u.used)
59 }
60
61 pub fn needs_zeroed_init(&self, name: InternedStr) -> bool {
63 self.names.get(&name).is_some_and(|u| u.addr_taken_before_assign)
64 }
65
66 pub fn mut_names(&self) -> HashSet<InternedStr> {
68 self.names
69 .iter()
70 .filter(|(_, u)| u.needs_mut)
71 .map(|(n, _)| *n)
72 .collect()
73 }
74
75 fn register(&mut self, name: InternedStr) {
76 self.names.entry(name).or_default();
77 }
78
79 fn mark_used(&mut self, name: InternedStr) {
80 if let Some(u) = self.names.get_mut(&name) {
81 u.used = true;
82 }
83 }
84
85 fn mark_mut(&mut self, name: InternedStr) {
86 if let Some(u) = self.names.get_mut(&name) {
87 u.needs_mut = true;
88 }
89 }
90
91 fn mark_addr_of(&mut self, name: InternedStr) {
92 if let Some(u) = self.names.get_mut(&name) {
93 u.needs_mut = true;
94 if !u.assigned_unconditionally {
95 u.addr_taken_before_assign = true;
96 }
97 }
98 }
99
100 fn mark_assigned(&mut self, name: InternedStr, unconditional: bool) {
101 if let Some(u) = self.names.get_mut(&name) {
102 u.needs_mut = true;
103 if unconditional {
104 u.assigned_unconditionally = true;
105 }
106 }
107 }
108}
109
110pub fn analyze_function(func_def: &FunctionDef) -> LocalUsageAnalysis {
113 let mut analysis = LocalUsageAnalysis::default();
114 for d in &func_def.declarator.derived {
115 if let DerivedDecl::Function(param_list) = d {
116 for p in ¶m_list.params {
117 if let Some(ref declarator) = p.declarator {
118 if let Some(param_name) = declarator.name {
119 analysis.register(param_name);
120 }
121 }
122 }
123 }
124 }
125 let mut walker = Walker { analysis: &mut analysis, cond_depth: 0 };
126 walker.items(&func_def.body.items);
127 analysis
128}
129
130pub fn analyze_macro(parse_result: &ParseResult, params: &[MacroParam]) -> LocalUsageAnalysis {
132 let mut analysis = LocalUsageAnalysis::default();
133 for p in params {
134 analysis.register(p.name);
135 }
136 let mut walker = Walker { analysis: &mut analysis, cond_depth: 0 };
137 match parse_result {
138 ParseResult::Expression(expr) => walker.expr(expr),
139 ParseResult::Statement(items) => walker.items(items),
140 ParseResult::Unparseable(_) => {}
141 }
142 analysis
143}
144
145struct Walker<'a> {
146 analysis: &'a mut LocalUsageAnalysis,
147 cond_depth: u32,
151}
152
153impl Walker<'_> {
154 fn items(&mut self, items: &[BlockItem]) {
155 for item in items {
156 match item {
157 BlockItem::Stmt(stmt) => self.stmt(stmt),
158 BlockItem::Decl(decl) => self.decl(decl),
159 }
160 }
161 }
162
163 fn decl(&mut self, decl: &Declaration) {
164 for init_decl in &decl.declarators {
165 if let Some(ref init) = init_decl.init {
168 self.initializer(init);
169 }
170 if let Some(name) = init_decl.declarator.name {
171 self.analysis.register(name);
172 if init_decl.init.is_some() {
173 if let Some(u) = self.analysis.names.get_mut(&name) {
174 if self.cond_depth == 0 {
175 u.assigned_unconditionally = true;
176 }
177 }
178 }
179 }
180 }
181 }
182
183 fn initializer(&mut self, init: &Initializer) {
184 match init {
185 Initializer::Expr(e) => self.expr(e),
186 Initializer::List(items) => self.initializer_items(items),
187 }
188 }
189
190 fn initializer_items(&mut self, items: &[InitializerItem]) {
191 for item in items {
192 self.initializer(&item.init);
193 }
194 }
195
196 fn stmt(&mut self, stmt: &Stmt) {
197 match stmt {
198 Stmt::Compound(compound) => self.compound(compound),
199 Stmt::Expr(Some(expr), _) => self.expr(expr),
200 Stmt::Expr(None, _) => {}
201 Stmt::If { cond, then_stmt, else_stmt, .. } => {
202 self.expr(cond);
203 self.cond_depth += 1;
204 self.stmt(then_stmt);
205 if let Some(else_s) = else_stmt {
206 self.stmt(else_s);
207 }
208 self.cond_depth -= 1;
209 }
210 Stmt::Switch { expr, body, .. } => {
211 self.expr(expr);
212 self.cond_depth += 1;
213 self.stmt(body);
214 self.cond_depth -= 1;
215 }
216 Stmt::While { cond, body, .. } => {
217 self.expr(cond);
219 self.cond_depth += 1;
220 self.stmt(body);
221 self.cond_depth -= 1;
222 }
223 Stmt::DoWhile { body, cond, .. } => {
224 self.stmt(body);
226 self.expr(cond);
227 }
228 Stmt::For { init, cond, step, body, .. } => {
229 match init {
230 Some(ForInit::Expr(e)) => self.expr(e),
231 Some(ForInit::Decl(decl)) => self.decl(decl),
232 None => {}
233 }
234 if let Some(c) = cond {
235 self.expr(c);
236 }
237 self.cond_depth += 1;
238 self.stmt(body);
239 if let Some(s) = step {
240 self.expr(s);
241 }
242 self.cond_depth -= 1;
243 }
244 Stmt::Return(Some(expr), _) => self.expr(expr),
245 Stmt::Return(None, _) => {}
246 Stmt::Label { stmt, .. } => self.stmt(stmt),
247 Stmt::Case { expr, stmt, .. } => {
248 self.expr(expr);
249 self.stmt(stmt);
250 }
251 Stmt::Default { stmt, .. } => self.stmt(stmt),
252 Stmt::Goto(..) | Stmt::Continue(..) | Stmt::Break(..) | Stmt::Asm { .. } => {}
253 }
254 }
255
256 fn compound(&mut self, compound: &CompoundStmt) {
257 self.items(&compound.items);
258 }
259
260 fn expr(&mut self, expr: &Expr) {
261 match &expr.kind {
262 ExprKind::Ident(name) => self.analysis.mark_used(*name),
263 ExprKind::AddrOf(inner) => {
264 if let ExprKind::Ident(name) = &inner.kind {
265 self.analysis.mark_addr_of(*name);
266 }
267 self.expr(inner);
268 }
269 ExprKind::Assign { op, lhs, rhs } => {
270 if let ExprKind::Ident(name) = &lhs.kind {
271 let unconditional = self.cond_depth == 0
272 && *op == crate::ast::AssignOp::Assign;
273 self.analysis.mark_assigned(*name, unconditional);
274 }
275 self.expr(lhs);
276 self.expr(rhs);
277 }
278 ExprKind::PreInc(inner) | ExprKind::PreDec(inner)
279 | ExprKind::PostInc(inner) | ExprKind::PostDec(inner) => {
280 if let ExprKind::Ident(name) = &inner.kind {
281 self.analysis.mark_mut(*name);
282 }
283 self.expr(inner);
284 }
285 ExprKind::Conditional { cond, then_expr, else_expr } => {
286 self.expr(cond);
287 self.cond_depth += 1;
288 self.expr(then_expr);
289 self.expr(else_expr);
290 self.cond_depth -= 1;
291 }
292 ExprKind::Binary { lhs, rhs, .. } | ExprKind::Comma { lhs, rhs } => {
293 self.expr(lhs);
294 self.expr(rhs);
295 }
296 ExprKind::Deref(inner)
297 | ExprKind::UnaryPlus(inner)
298 | ExprKind::UnaryMinus(inner)
299 | ExprKind::BitNot(inner)
300 | ExprKind::LogNot(inner)
301 | ExprKind::Sizeof(inner)
302 | ExprKind::Cast { expr: inner, .. } => self.expr(inner),
303 ExprKind::Index { expr: base, index } => {
304 self.expr(base);
305 self.expr(index);
306 }
307 ExprKind::Call { func, args } => {
308 self.expr(func);
309 for arg in args {
310 self.expr(arg);
311 }
312 }
313 ExprKind::MacroCall { expanded, args, .. } => {
314 self.expr(expanded);
317 for arg in args {
318 self.expr(arg);
319 }
320 }
321 ExprKind::BuiltinCall { args, .. } => {
322 for arg in args {
323 if let BuiltinArg::Expr(e) = arg {
324 self.expr(e);
325 }
326 }
327 }
328 ExprKind::Member { expr: inner, .. } | ExprKind::PtrMember { expr: inner, .. } => {
329 self.expr(inner);
330 }
331 ExprKind::StmtExpr(compound) => self.compound(compound),
332 ExprKind::Assert { condition, .. } => self.expr(condition),
333 ExprKind::CompoundLit { init, .. } => self.initializer_items(init),
334 ExprKind::IntLit(_)
335 | ExprKind::UIntLit(_)
336 | ExprKind::FloatLit(_)
337 | ExprKind::CharLit(_)
338 | ExprKind::StringLit(_)
339 | ExprKind::SizeofType(_)
340 | ExprKind::Alignof(_) => {}
341 }
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use crate::ast::{AssignOp, Stmt};
349 use crate::intern::StringInterner;
350 use crate::source::SourceLocation;
351
352 fn ident(name: InternedStr) -> Expr {
353 Expr::new(ExprKind::Ident(name), SourceLocation::default())
354 }
355
356 fn int_lit(n: i64) -> Expr {
357 Expr::new(ExprKind::IntLit(n), SourceLocation::default())
358 }
359
360 fn assign(name: InternedStr, value: Expr) -> Expr {
361 Expr::new(
362 ExprKind::Assign {
363 op: AssignOp::Assign,
364 lhs: Box::new(ident(name)),
365 rhs: Box::new(value),
366 },
367 SourceLocation::default(),
368 )
369 }
370
371 fn addr_of(name: InternedStr) -> Expr {
372 Expr::new(ExprKind::AddrOf(Box::new(ident(name))), SourceLocation::default())
373 }
374
375 fn expr_stmt(e: Expr) -> Stmt {
376 Stmt::Expr(Some(Box::new(e)), SourceLocation::default())
377 }
378
379 fn macro_params(names: &[InternedStr]) -> Vec<MacroParam> {
380 names.iter().map(|n| MacroParam::new(*n, SourceLocation::default())).collect()
381 }
382
383 fn analyze_stmts(stmts: Vec<Stmt>, params: &[MacroParam]) -> LocalUsageAnalysis {
384 let items = stmts.into_iter().map(BlockItem::Stmt).collect();
385 analyze_macro(&ParseResult::Statement(items), params)
386 }
387
388 #[test]
391 fn test_reassign_in_nested_loop_needs_mut() {
392 let mut interner = StringInterner::new();
393 let s = interner.intern("s");
394 let params = macro_params(&[s]);
395 let body = Stmt::Compound(CompoundStmt {
397 items: vec![BlockItem::Stmt(expr_stmt(assign(s, int_lit(1))))],
398 info: Default::default(),
399 });
400 let stmts = vec![Stmt::While {
401 cond: Box::new(ident(s)),
402 body: Box::new(Stmt::Compound(CompoundStmt {
403 items: vec![BlockItem::Stmt(body)],
404 info: Default::default(),
405 })),
406 loc: SourceLocation::default(),
407 }];
408 let analysis = analyze_stmts(stmts, ¶ms);
409 assert!(analysis.needs_mut(s));
410 assert!(!analysis.is_unused(s));
411 }
412
413 #[test]
415 fn test_unused_param() {
416 let mut interner = StringInterner::new();
417 let used = interner.intern("used");
418 let unused = interner.intern("unused");
419 let params = macro_params(&[used, unused]);
420 let stmts = vec![expr_stmt(ident(used))];
421 let analysis = analyze_stmts(stmts, ¶ms);
422 assert!(!analysis.is_unused(used));
423 assert!(analysis.is_unused(unused));
424 let other = interner.intern("other");
426 assert!(!analysis.is_unused(other));
427 }
428
429 #[test]
432 fn test_addr_taken_before_assign() {
433 let mut interner = StringInterner::new();
434 let out = interner.intern("out");
435 let ok = interner.intern("ok");
436 let params = macro_params(&[out, ok]);
437 let stmts = vec![
439 expr_stmt(addr_of(out)),
440 expr_stmt(assign(out, int_lit(1))),
441 expr_stmt(assign(ok, int_lit(2))),
442 expr_stmt(addr_of(ok)),
443 ];
444 let analysis = analyze_stmts(stmts, ¶ms);
445 assert!(analysis.needs_zeroed_init(out));
446 assert!(!analysis.needs_zeroed_init(ok));
447 assert!(analysis.needs_mut(out));
449 assert!(analysis.needs_mut(ok));
450 }
451
452 #[test]
455 fn test_conditional_assign_does_not_clear_window() {
456 let mut interner = StringInterner::new();
457 let x = interner.intern("x");
458 let c = interner.intern("c");
459 let params = macro_params(&[x, c]);
460 let stmts = vec![
461 Stmt::If {
462 cond: Box::new(ident(c)),
463 then_stmt: Box::new(expr_stmt(assign(x, int_lit(1)))),
464 else_stmt: None,
465 loc: SourceLocation::default(),
466 },
467 expr_stmt(addr_of(x)),
468 ];
469 let analysis = analyze_stmts(stmts, ¶ms);
470 assert!(analysis.needs_zeroed_init(x));
471 }
472}