refactor(joinir): Phase 89 リファクタリング - 3) Frontend再帰探索

変更内容:
- Break/Continue/Return 検出を再帰的探索に変更
  - has_break_in_loop_body(): top-level If のみ → 再帰的探索
  - has_continue_in_loop_body(): top-level If のみ → 再帰的探索
  - has_return_in_loop_body(): top-level If のみ → 再帰的探索
- ネストした If/Block 内のステートメントも検出可能に
- ヘルパー関数追加:
  - has_break_recursive()
  - has_continue_recursive()
  - has_return_recursive()

理由:
- Phase 90+ で合成形パターン(ネストした If/Block)に対応する準備
- 現状では top-level のみチェックしているため、深い階層で誤判定の可能性

影響範囲:
- loop_frontend_binding.rs のルーティングロジック
- Phase 90 の合成形パターンで正確な検出が可能に

テスト結果:
- lib tests: 993 passed (回帰なし)
- normalized_dev tests: 61 passed / 1 failed (ベースライン維持)

Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-14 03:13:05 +09:00
parent 98a0b8396f
commit 90c45e544b

View File

@ -345,10 +345,10 @@ impl AstToJoinIrLowerer {
vars
}
/// Phase 85: Loop body に Break があるかチェック
/// Phase 85: Loop body に Break があるかチェック(再帰的探索)
///
/// ループパターン検出loop_frontend_bindingで使用される。
/// If文内のBreakステートメント検出する。
/// ネストした If/Block 内の Break ステートメント検出する。
///
/// # Arguments
/// * `loop_body` - ループ本体のステートメント配列
@ -356,33 +356,46 @@ impl AstToJoinIrLowerer {
/// # Returns
/// ループ内にBreakがあればtrue
pub(crate) fn has_break_in_loop_body(loop_body: &[serde_json::Value]) -> bool {
loop_body.iter().any(|stmt| {
if stmt["type"].as_str() == Some("If") {
let then_has = stmt["then"]
.as_array()
.map(|body| {
body.iter()
.any(|s| s["type"].as_str() == Some("Break"))
})
.unwrap_or(false);
let else_has = stmt["else"]
.as_array()
.map(|body| {
body.iter()
.any(|s| s["type"].as_str() == Some("Break"))
})
.unwrap_or(false);
then_has || else_has
} else {
false
}
})
Self::has_break_recursive(loop_body)
}
/// Phase 85: Loop body に Continue があるかチェック
/// 再帰的に Break を探索
fn has_break_recursive(stmts: &[serde_json::Value]) -> bool {
for stmt in stmts {
match stmt["type"].as_str() {
Some("Break") => return true,
Some("If") => {
// then 分岐を再帰探索
if let Some(then_body) = stmt["then"].as_array() {
if Self::has_break_recursive(then_body) {
return true;
}
}
// else 分岐を再帰探索
if let Some(else_body) = stmt["else"].as_array() {
if Self::has_break_recursive(else_body) {
return true;
}
}
}
Some("Block") => {
// Block 内を再帰探索
if let Some(block_body) = stmt["body"].as_array() {
if Self::has_break_recursive(block_body) {
return true;
}
}
}
_ => {}
}
}
false
}
/// Phase 85: Loop body に Continue があるかチェック(再帰的探索)
///
/// ループパターン検出loop_frontend_bindingで使用される。
/// If文内のContinueステートメント検出する。
/// ネストした If/Block 内の Continue ステートメント検出する。
///
/// # Arguments
/// * `loop_body` - ループ本体のステートメント配列
@ -390,33 +403,46 @@ impl AstToJoinIrLowerer {
/// # Returns
/// ループ内にContinueがあればtrue
pub(crate) fn has_continue_in_loop_body(loop_body: &[serde_json::Value]) -> bool {
loop_body.iter().any(|stmt| {
if stmt["type"].as_str() == Some("If") {
let then_has = stmt["then"]
.as_array()
.map(|body| {
body.iter()
.any(|s| s["type"].as_str() == Some("Continue"))
})
.unwrap_or(false);
let else_has = stmt["else"]
.as_array()
.map(|body| {
body.iter()
.any(|s| s["type"].as_str() == Some("Continue"))
})
.unwrap_or(false);
then_has || else_has
} else {
false
}
})
Self::has_continue_recursive(loop_body)
}
/// Phase 89: Loop body に Return があるかチェック
/// 再帰的に Continue を探索
fn has_continue_recursive(stmts: &[serde_json::Value]) -> bool {
for stmt in stmts {
match stmt["type"].as_str() {
Some("Continue") => return true,
Some("If") => {
// then 分岐を再帰探索
if let Some(then_body) = stmt["then"].as_array() {
if Self::has_continue_recursive(then_body) {
return true;
}
}
// else 分岐を再帰探索
if let Some(else_body) = stmt["else"].as_array() {
if Self::has_continue_recursive(else_body) {
return true;
}
}
}
Some("Block") => {
// Block 内を再帰探索
if let Some(block_body) = stmt["body"].as_array() {
if Self::has_continue_recursive(block_body) {
return true;
}
}
}
_ => {}
}
}
false
}
/// Phase 89: Loop body に Return があるかチェック(再帰的探索)
///
/// ループパターン検出loop_frontend_bindingで使用される。
/// If文内のReturnステートメント検出するloop-internal early return
/// ネストした If/Block 内の Return ステートメント検出するloop-internal early return
///
/// # Arguments
/// * `loop_body` - ループ本体のステートメント配列
@ -424,26 +450,39 @@ impl AstToJoinIrLowerer {
/// # Returns
/// ループ内にReturnがあればtrue
pub(crate) fn has_return_in_loop_body(loop_body: &[serde_json::Value]) -> bool {
loop_body.iter().any(|stmt| {
if stmt["type"].as_str() == Some("If") {
let then_has = stmt["then"]
.as_array()
.map(|body| {
body.iter()
.any(|s| s["type"].as_str() == Some("Return"))
})
.unwrap_or(false);
let else_has = stmt["else"]
.as_array()
.map(|body| {
body.iter()
.any(|s| s["type"].as_str() == Some("Return"))
})
.unwrap_or(false);
then_has || else_has
} else {
false
Self::has_return_recursive(loop_body)
}
/// 再帰的に Return を探索
fn has_return_recursive(stmts: &[serde_json::Value]) -> bool {
for stmt in stmts {
match stmt["type"].as_str() {
Some("Return") => return true,
Some("If") => {
// then 分岐を再帰探索
if let Some(then_body) = stmt["then"].as_array() {
if Self::has_return_recursive(then_body) {
return true;
}
}
// else 分岐を再帰探索
if let Some(else_body) = stmt["else"].as_array() {
if Self::has_return_recursive(else_body) {
return true;
}
}
}
Some("Block") => {
// Block 内を再帰探索
if let Some(block_body) = stmt["body"].as_array() {
if Self::has_return_recursive(block_body) {
return true;
}
}
}
_ => {}
}
})
}
false
}
}