Skip to main content

libperl_macrogen/
local_usage.rs

1//! Phase 2: 関数/マクロ本体の名前 (パラメータ・局所変数) 使用解析
2//!
3//! パラメータと局所変数それぞれについて、以下を **一度の走査** で判定する:
4//!
5//! - `needs_mut` — 再代入・複合代入・増減・AddrOf があるか (GH #16: `let mut` / `mut` param)
6//! - `used` — 1 回でも参照されたか (GH #22: 未使用なら `_` prefix)
7//! - `addr_taken_before_assign` — 無条件の初期化より前にアドレスを取られたか
8//!   (GH #23: 未初期化宣言の out-param パターン → zeroed 初期化)
9//!
10//! Pass Separation Rule に従い解析は本モジュール (Phase 2) に置き、
11//! Phase 3 (rust_codegen) は結果を読むだけにする。旧 `collect_mut_params`
12//! 系 (rust_codegen.rs) の置き換えであり、旧実装が落としていた
13//! Switch/Label/Case/Default・ネストした宣言・ForInit::Decl も走査する。
14//!
15//! 名前の登録は走査順 (= ソース順) に行うため、C の「宣言より前の同名参照」
16//! は外側スコープの別実体として自然に無視される。シャドウイングは名前単位で
17//! マージする (保守的)。
18
19use 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/// 1 つの名前 (パラメータまたは局所変数) の使用状況
29#[derive(Debug, Default, Clone)]
30pub struct NameUsage {
31    /// 読み書きいずれかで 1 回でも参照されたか
32    pub used: bool,
33    /// 再代入・複合代入・増減・AddrOf があり `mut` 宣言が必要か
34    pub needs_mut: bool,
35    /// 無条件の初期化 (宣言時初期化子 or 無条件文脈の単純代入) より前に
36    /// アドレスを取られたか。未初期化宣言でこれが立つ場合、deferred
37    /// initialization (`let x: T;`) では E0381 になるため zeroed 初期化が必要
38    pub addr_taken_before_assign: bool,
39    /// 走査内部状態: 無条件文脈 (If/Switch/ループ本体/三項の外) で
40    /// 単純代入または宣言時初期化があったか
41    assigned_unconditionally: bool,
42}
43
44/// 本体走査の結果 (名前 → NameUsage)
45#[derive(Debug, Default, Clone)]
46pub struct LocalUsageAnalysis {
47    names: HashMap<InternedStr, NameUsage>,
48}
49
50impl LocalUsageAnalysis {
51    /// `mut` 宣言が必要か
52    pub fn needs_mut(&self, name: InternedStr) -> bool {
53        self.names.get(&name).is_some_and(|u| u.needs_mut)
54    }
55
56    /// 追跡対象でありかつ一度も参照されていないか (→ `_` prefix)
57    pub fn is_unused(&self, name: InternedStr) -> bool {
58        self.names.get(&name).is_some_and(|u| !u.used)
59    }
60
61    /// 未初期化宣言に zeroed 初期化が必要か (代入前 AddrOf があったか)
62    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    /// `needs_mut` な名前の集合 (旧 `collect_mut_params` の戻り値互換)
67    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
110/// inline 関数 (FunctionDef) を解析。
111/// パラメータと、ネストも含む全 `BlockItem::Decl` の局所変数が対象。
112pub 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 &param_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
130/// マクロ (ParseResult) を仮引数付きで解析。
131pub 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    /// If/Switch/ループ本体/三項演算子の内側の深さ。
148    /// > 0 の間の単純代入は「無条件の初期化」と見なさない
149    /// (`if c { x = 1 } use(&x)` の x を E0381 から救うための保守判定)
150    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            // 初期化子は自身の登録より先に走査する
166            // (`int x = x0;` の x0 参照、シャドウ元の同名参照を正しく数える)
167            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                // 条件は少なくとも 1 回無条件に評価される
218                self.expr(cond);
219                self.cond_depth += 1;
220                self.stmt(body);
221                self.cond_depth -= 1;
222            }
223            Stmt::DoWhile { body, cond, .. } => {
224                // do-while の本体と条件は少なくとも 1 回無条件に実行される
225                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                // used / mut は展開結果と元引数の両方から拾う
315                // (codegen はどちらの形でも emit しうるため保守的に併合)
316                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    /// ループ本体内の再代入 (Perl_isC9_STRICT_UTF8_CHAR 型の E0384) を
389    /// ネスト越しに検出できること
390    #[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        // while (s) { { s = 1; } }
396        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, &params);
409        assert!(analysis.needs_mut(s));
410        assert!(!analysis.is_unused(s));
411    }
412
413    /// 本体で参照されないパラメータを未使用と判定できること
414    #[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, &params);
422        assert!(!analysis.is_unused(used));
423        assert!(analysis.is_unused(unused));
424        // 追跡外の名前は「未使用」と主張しない
425        let other = interner.intern("other");
426        assert!(!analysis.is_unused(other));
427    }
428
429    /// 代入前の AddrOf (out-param パターン) は zeroed 初期化が必要、
430    /// 無条件代入後の AddrOf は不要
431    #[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        // foo(&out); out = 1; ok = 2; bar(&ok);
438        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, &params);
445        assert!(analysis.needs_zeroed_init(out));
446        assert!(!analysis.needs_zeroed_init(ok));
447        // AddrOf は mut 扱い (既存 collect_mut_params 互換)
448        assert!(analysis.needs_mut(out));
449        assert!(analysis.needs_mut(ok));
450    }
451
452    /// 条件付きの代入は「無条件の初期化」と見なさない
453    /// (`if c { x = 1 } use(&x)` は E0381 になりうるので zeroed が必要)
454    #[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, &params);
470        assert!(analysis.needs_zeroed_init(x));
471    }
472}