Files
hakorune/tools/hako_check/rules/rule_dead_blocks.hako
nyash-codex 000335c32e feat(hako_check): Phase 154 MIR CFG integration & HC020 dead block detection
Implements block-level unreachable code detection using MIR CFG information.
Complements Phase 153's method-level HC019 with fine-grained analysis.

Core Infrastructure (Complete):
- CFG Extractor: Extract block reachability from MirModule
- DeadBlockAnalyzerBox: HC020 rule for unreachable blocks
- CLI Integration: --dead-blocks flag and rule execution
- Test Cases: 4 comprehensive patterns (early return, constant false, infinite loop, break)
- Smoke Test: Validation script for all test cases

Implementation Details:
- src/mir/cfg_extractor.rs: New module for CFG→JSON extraction
- tools/hako_check/rules/rule_dead_blocks.hako: HC020 analyzer box
- tools/hako_check/cli.hako: Added --dead-blocks flag and HC020 integration
- apps/tests/hako_check/test_dead_blocks_*.hako: 4 test cases

Architecture:
- Follows Phase 153 boxed modular pattern (DeadCodeAnalyzerBox)
- Optional CFG field in Analysis IR (backward compatible)
- Uses MIR's built-in reachability computation
- Gracefully skips if CFG unavailable

Known Limitation:
- CFG data bridge pending (Phase 155): analysis_consumer.hako needs MIR access
- Current: DeadBlockAnalyzerBox implemented, but CFG not yet in Analysis IR
- Estimated 2-3 hours to complete bridge in Phase 155

Test Coverage:
- Unit tests: cfg_extractor (simple CFG, unreachable blocks)
- Integration tests: 4 test cases ready (will activate with bridge)
- Smoke test: tools/hako_check_deadblocks_smoke.sh

Documentation:
- phase154_mir_cfg_inventory.md: CFG structure investigation
- phase154_implementation_summary.md: Complete implementation guide
- hako_check_design.md: HC020 rule documentation

Next Phase 155:
- Implement CFG data bridge (extract_mir_cfg builtin)
- Update analysis_consumer.hako to call bridge
- Activate HC020 end-to-end testing

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:00:45 +09:00

108 lines
2.7 KiB
Plaintext

// tools/hako_check/rules/rule_dead_blocks.hako — HC020: Unreachable Basic Block Detection
// Block-level dead code analyzer using MIR CFG information.
// Phase 154: MIR CFG integration for fine-grained unreachable code detection.
static box DeadBlockAnalyzerBox {
// Main entry point for unreachable block analysis
// Input: ir (Analysis IR with CFG), path (file path), out (diagnostics array)
// Returns: void (like other rules)
method apply_ir(ir, path, out) {
if ir == null { return }
if out == null { return }
// Phase 154: Requires CFG information from MIR
local cfg = ir.get("cfg")
if cfg == null {
// CFG info not available - skip analysis
return
}
local functions = cfg.get("functions")
if functions == null || functions.size() == 0 { return }
// Analyze each function's blocks
local i = 0
while i < functions.size() {
me._analyze_function_blocks(functions.get(i), path, out)
i = i + 1
}
return
}
// Analyze blocks within a single function
_analyze_function_blocks(func, path, out) {
if func == null { return }
local func_name = func.get("name")
local blocks = func.get("blocks")
if blocks == null || blocks.size() == 0 { return }
// Scan for unreachable blocks
local bi = 0
while bi < blocks.size() {
local block = blocks.get(bi)
if block == null { bi = bi + 1; continue }
local block_id = block.get("id")
local reachable = block.get("reachable")
// Report unreachable blocks (HC020)
if reachable == 0 {
local terminator = block.get("terminator")
local reason = me._infer_unreachable_reason(terminator)
local msg = "[HC020] Unreachable basic block: fn=" + func_name
+ " bb=" + me._itoa(block_id)
if reason != null && reason != "" {
msg = msg + " (" + reason + ")"
}
out.push(msg + " :: " + path)
}
bi = bi + 1
}
return
}
// Infer reason for unreachability based on terminator type
_infer_unreachable_reason(terminator) {
if terminator == null { return "no terminator" }
// Common patterns
if terminator == "Return" { return "after early return" }
if terminator == "Jump" { return "unreachable branch" }
if terminator == "Branch" { return "dead conditional" }
return ""
}
// Helper: integer to string
_itoa(n) {
local v = 0 + n
if v == 0 { return "0" }
local out = ""
local digits = "0123456789"
local tmp = ""
while v > 0 {
local d = v % 10
tmp = digits.substring(d, d+1) + tmp
v = v / 10
}
out = tmp
return out
}
}
static box RuleDeadBlocksMain {
method main(args) {
return 0
}
}