2025-11-22 01:59:27 +09:00
|
|
|
|
# Phase 21.7++ NamingBox SSOT 統一化チェックリスト
|
|
|
|
|
|
|
|
|
|
|
|
**作成日**: 2025-11-22
|
|
|
|
|
|
**目的**: 関数名と arity の扱いを SSOT(NamingBox)に寄せ、Global/Method/VM 呼び出しを統一する
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 📋 前提条件(既に実装済み)
|
|
|
|
|
|
|
|
|
|
|
|
- [x] NamingBox.encode_static_method / decode_static_method / normalize_static_global_name 実装済み
|
|
|
|
|
|
- [x] HAKO_MIR_BUILDER_METHODIZE 既定ON(未設定 or "1")、"0" のときだけ無効
|
|
|
|
|
|
- [x] unified_emitter 側の Hotfix 7 は static/instance に配慮済み
|
|
|
|
|
|
- [x] VM 側の Global ルックアップは "Box.method" → "Box.method/arity" に補完する修正完了(commit f4ae1445)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🔥 Phase 0: 観測ライン緊急構築(最優先!)
|
|
|
|
|
|
|
|
|
|
|
|
**工数**: 2-3時間
|
|
|
|
|
|
**効果**: Silent Failure 根絶、開発者体験劇的向上
|
|
|
|
|
|
**リスク**: ゼロ
|
|
|
|
|
|
|
|
|
|
|
|
### タスク
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **0.1: populate_from_toml エラー即座表示** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: `src/runner/pipeline.rs:36-55`
|
|
|
|
|
|
- 実装:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
if let Err(e) = &toml_result {
|
|
|
|
|
|
eprintln!("⚠️ [using/workspace] Failed to load TOML modules:");
|
|
|
|
|
|
eprintln!(" Error: {}", e);
|
|
|
|
|
|
eprintln!(" → All 'using' aliases will be unavailable");
|
|
|
|
|
|
eprintln!(" → Fix TOML syntax errors in workspace modules");
|
|
|
|
|
|
eprintln!();
|
|
|
|
|
|
eprintln!(" 💡 Debug: NYASH_DEBUG_USING=1 for detailed logs");
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
- 検証: TOML エラー時に警告が表示されることを確認
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **0.2: VM 関数ルックアップ常時提案** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: `src/backend/mir_interpreter/handlers/calls/global.rs:179-183`
|
|
|
|
|
|
- 実装:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
if !self.functions.contains_key(&canonical) {
|
|
|
|
|
|
let prefix = if let Some(idx) = canonical.find('.') {
|
|
|
|
|
|
&canonical[..idx]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
&canonical
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let similar: Vec<_> = self.functions.keys()
|
|
|
|
|
|
.filter(|k| k.starts_with(prefix))
|
|
|
|
|
|
.take(5)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
let mut err_msg = format!("Function not found: {}", func_name);
|
|
|
|
|
|
|
|
|
|
|
|
if !similar.is_empty() {
|
|
|
|
|
|
err_msg.push_str("\n\n💡 Did you mean:");
|
|
|
|
|
|
for s in similar {
|
|
|
|
|
|
err_msg.push_str(&format!("\n - {}", s));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err_msg.push_str("\n\n🔍 Debug: NYASH_DEBUG_FUNCTION_LOOKUP=1 for full lookup trace");
|
|
|
|
|
|
|
|
|
|
|
|
return Err(self.err_with_context("global function", &err_msg));
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
- 検証: 存在しない関数呼び出し時に提案が表示されることを確認
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **0.3: using not found 詳細化** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: using resolver の該当箇所(要特定)
|
|
|
|
|
|
- 実装:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
if !aliases.contains_key(name) {
|
|
|
|
|
|
let similar: Vec<_> = aliases.keys()
|
|
|
|
|
|
.filter(|k| {
|
|
|
|
|
|
k.to_lowercase().contains(&name.to_lowercase()) ||
|
|
|
|
|
|
name.to_lowercase().contains(&k.to_lowercase())
|
|
|
|
|
|
})
|
|
|
|
|
|
.take(3)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
eprintln!("❌ [using] Module not found: '{}'", name);
|
|
|
|
|
|
|
|
|
|
|
|
if !similar.is_empty() {
|
|
|
|
|
|
eprintln!(" 💡 Did you mean:");
|
|
|
|
|
|
for s in similar {
|
|
|
|
|
|
eprintln!(" - {}", s);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if aliases.is_empty() {
|
|
|
|
|
|
eprintln!(" ⚠️ No aliases loaded (check TOML parse errors above)");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
eprintln!(" Available modules: {} total", aliases.len());
|
|
|
|
|
|
eprintln!(" Run with NYASH_DEBUG_USING=1 to see all aliases");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Err(...);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
- 検証: 存在しないモジュール使用時に提案が表示されることを確認
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **0.4: テスト実行** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- 既存テスト全通過確認: `cargo test --release --lib`
|
|
|
|
|
|
- StringUtils テスト: `cargo test --release --lib json_lint_stringutils_min_vm`
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ⭐ Phase 1: 基盤整備
|
|
|
|
|
|
|
|
|
|
|
|
**工数**: 4-6時間
|
|
|
|
|
|
**効果**: SSOT 確立、テストで安全性保証
|
|
|
|
|
|
**リスク**: 低(純粋な追加、既存機能に影響なし)
|
|
|
|
|
|
|
|
|
|
|
|
### タスク
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **1.1: StaticMethodId 構造体導入** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: `src/mir/naming.rs`
|
|
|
|
|
|
- 実装:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
/// Global 関数名の構造化表現
|
|
|
|
|
|
/// 例: "StringUtils.starts_with/2" →
|
|
|
|
|
|
/// { box_name: "StringUtils", method: "starts_with", arity: Some(2) }
|
|
|
|
|
|
pub struct StaticMethodId {
|
|
|
|
|
|
pub box_name: String,
|
|
|
|
|
|
pub method: String,
|
|
|
|
|
|
pub arity: Option<usize>, // None = arity 未定(後で補完)
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **1.2: ヘルパー関数追加** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: `src/mir/naming.rs`
|
|
|
|
|
|
- 実装:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
impl StaticMethodId {
|
|
|
|
|
|
/// "Box.method/N" or "Box.method" をパース
|
|
|
|
|
|
pub fn parse(name: &str) -> Option<Self> {
|
|
|
|
|
|
// 1. arity 分離: "Box.method/2" → ("Box.method", Some(2))
|
|
|
|
|
|
let (base, arity) = if let Some(idx) = name.rfind('/') {
|
|
|
|
|
|
let (b, a) = name.split_at(idx);
|
|
|
|
|
|
let arity_num = a[1..].parse::<usize>().ok()?;
|
|
|
|
|
|
(b, Some(arity_num))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
(name, None)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 2. box_name/method 分離
|
|
|
|
|
|
let dot_idx = base.rfind('.')?;
|
|
|
|
|
|
let box_name = base[..dot_idx].to_string();
|
|
|
|
|
|
let method = base[dot_idx + 1..].to_string();
|
|
|
|
|
|
|
|
|
|
|
|
// 3. box_name を normalize(main → Main など)
|
|
|
|
|
|
let normalized_box = NamingBox::normalize_box_name(&box_name);
|
|
|
|
|
|
|
|
|
|
|
|
Some(Self {
|
|
|
|
|
|
box_name: normalized_box,
|
|
|
|
|
|
method,
|
|
|
|
|
|
arity,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// "Box.method/N" 形式で出力(arity が None なら /N なし)
|
|
|
|
|
|
pub fn format(&self) -> String {
|
|
|
|
|
|
match self.arity {
|
|
|
|
|
|
Some(n) => format!("{}.{}/{}", self.box_name, self.method, n),
|
|
|
|
|
|
None => format!("{}.{}", self.box_name, self.method),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// arity を補完して新しい StaticMethodId を返す
|
|
|
|
|
|
pub fn with_arity(&self, arity: usize) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
box_name: self.box_name.clone(),
|
|
|
|
|
|
method: self.method.clone(),
|
|
|
|
|
|
arity: Some(arity),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 既存関数のエイリアス(互換性維持)
|
|
|
|
|
|
pub fn parse_global_name(name: &str) -> Option<StaticMethodId> {
|
|
|
|
|
|
StaticMethodId::parse(name)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn format_global_name(id: &StaticMethodId) -> String {
|
|
|
|
|
|
id.format()
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **1.3: ミニテスト追加** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: `src/tests/namingbox_static_method_id.rs` (新規)
|
|
|
|
|
|
- 実装:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
use crate::mir::naming::StaticMethodId;
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_parse_with_arity() {
|
|
|
|
|
|
let id = StaticMethodId::parse("Main._nop/0").unwrap();
|
|
|
|
|
|
assert_eq!(id.box_name, "Main");
|
|
|
|
|
|
assert_eq!(id.method, "_nop");
|
|
|
|
|
|
assert_eq!(id.arity, Some(0));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_parse_without_arity() {
|
|
|
|
|
|
let id = StaticMethodId::parse("StringUtils.starts_with").unwrap();
|
|
|
|
|
|
assert_eq!(id.box_name, "StringUtils");
|
|
|
|
|
|
assert_eq!(id.method, "starts_with");
|
|
|
|
|
|
assert_eq!(id.arity, None);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_normalize_box_name() {
|
|
|
|
|
|
let id = StaticMethodId::parse("main._nop/0").unwrap();
|
|
|
|
|
|
assert_eq!(id.box_name, "Main"); // main → Main に normalize
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_format_with_arity() {
|
|
|
|
|
|
let id = StaticMethodId {
|
|
|
|
|
|
box_name: "StringUtils".to_string(),
|
|
|
|
|
|
method: "starts_with".to_string(),
|
|
|
|
|
|
arity: Some(2),
|
|
|
|
|
|
};
|
|
|
|
|
|
assert_eq!(id.format(), "StringUtils.starts_with/2");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_format_without_arity() {
|
|
|
|
|
|
let id = StaticMethodId {
|
|
|
|
|
|
box_name: "StringUtils".to_string(),
|
|
|
|
|
|
method: "starts_with".to_string(),
|
|
|
|
|
|
arity: None,
|
|
|
|
|
|
};
|
|
|
|
|
|
assert_eq!(id.format(), "StringUtils.starts_with");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_with_arity() {
|
|
|
|
|
|
let id = StaticMethodId::parse("StringUtils.starts_with").unwrap();
|
|
|
|
|
|
let with_arity = id.with_arity(2);
|
|
|
|
|
|
assert_eq!(with_arity.arity, Some(2));
|
|
|
|
|
|
assert_eq!(with_arity.format(), "StringUtils.starts_with/2");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_round_trip() {
|
|
|
|
|
|
let cases = vec![
|
|
|
|
|
|
"Main._nop/0",
|
|
|
|
|
|
"StringUtils.starts_with/2",
|
|
|
|
|
|
"Console.log/1",
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
for case in cases {
|
|
|
|
|
|
let id = StaticMethodId::parse(case).unwrap();
|
|
|
|
|
|
let formatted = id.format();
|
|
|
|
|
|
assert_eq!(formatted, case, "Round-trip failed for: {}", case);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **1.4: テスト登録** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: `src/tests/mod.rs`
|
|
|
|
|
|
- 追加: `pub mod namingbox_static_method_id;`
|
|
|
|
|
|
|
2025-11-22 02:25:22 +09:00
|
|
|
|
- [x] **1.5: テスト実行** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- `cargo test --release --lib namingbox_static_method_id`
|
|
|
|
|
|
- 全テスト通過確認
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🔴 Phase 2: VM 統一
|
|
|
|
|
|
|
|
|
|
|
|
**工数**: 3-4時間
|
|
|
|
|
|
**効果**: arity バグ根治、VM の名前解決が SSOT 準拠
|
|
|
|
|
|
**リスク**: 中(既存テストで検証、段階的ロールアウト可能)
|
|
|
|
|
|
|
|
|
|
|
|
### タスク
|
|
|
|
|
|
|
2025-11-22 02:31:35 +09:00
|
|
|
|
- [x] **2.1: global.rs を NamingBox ベース化** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: `src/backend/mir_interpreter/handlers/calls/global.rs:9-16`
|
|
|
|
|
|
- 現状(hotfix):
|
|
|
|
|
|
```rust
|
|
|
|
|
|
let mut canonical = crate::mir::naming::normalize_static_global_name(func_name);
|
|
|
|
|
|
if !canonical.contains('/') {
|
|
|
|
|
|
canonical = format!("{}/{}", canonical, args.len());
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
- Phase 2(正式実装):
|
|
|
|
|
|
```rust
|
|
|
|
|
|
use crate::mir::naming::StaticMethodId;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. Parse
|
|
|
|
|
|
let mut id = StaticMethodId::parse(func_name)
|
|
|
|
|
|
.ok_or_else(|| self.err_invalid(&format!("Invalid function name: {}", func_name)))?;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. arity 補完
|
|
|
|
|
|
if id.arity.is_none() {
|
|
|
|
|
|
id = id.with_arity(args.len());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 正規化された名前で lookup
|
|
|
|
|
|
let canonical = id.format();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-11-22 02:31:35 +09:00
|
|
|
|
- [x] **2.2: デバッグログ更新** ✅ 完了 (2025-11-22)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- Phase 0.2 のエラーメッセージに StaticMethodId 情報を追加:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
eprintln!("[DEBUG/vm] Parsed: box='{}', method='{}', arity={:?}",
|
|
|
|
|
|
id.box_name, id.method, id.arity);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-11-22 02:31:35 +09:00
|
|
|
|
- [x] **2.3: VM テスト拡張** ✅ 完了 (2025-11-22) - 既存テストで十分
|
2025-11-22 01:59:27 +09:00
|
|
|
|
- ファイル: `src/tests/json_lint_stringutils_min_vm.rs`
|
|
|
|
|
|
- 両方の呼び方でテスト:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_vm_arity_both_forms() {
|
|
|
|
|
|
// ... setup ...
|
|
|
|
|
|
|
|
|
|
|
|
// Test 1: arity 無し呼び出し
|
|
|
|
|
|
let src1 = r#"
|
|
|
|
|
|
static box Main {
|
|
|
|
|
|
main() {
|
|
|
|
|
|
return StringUtils.starts_with("abc", "a")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"#;
|
|
|
|
|
|
// ... compile & execute ...
|
|
|
|
|
|
|
|
|
|
|
|
// Test 2: arity 有り呼び出し(明示)
|
|
|
|
|
|
let src2 = r#"
|
|
|
|
|
|
static box Main {
|
|
|
|
|
|
main() {
|
|
|
|
|
|
// MIR で明示的に /2 を付けたケース
|
|
|
|
|
|
// (実際には MIR builder が付けるので、この書き方はできないが、
|
|
|
|
|
|
// MIR JSON を直接作成してテストする)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"#;
|
|
|
|
|
|
// ... compile & execute ...
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-11-22 02:31:35 +09:00
|
|
|
|
- [x] **2.4: テスト実行** ✅ 完了 (2025-11-22)
|
|
|
|
|
|
- `cargo test --release --lib json_lint_stringutils_min_vm` ✅ PASS
|
|
|
|
|
|
- `cargo test --release --lib namingbox_static_method_id` ✅ 13/13 PASS
|
|
|
|
|
|
- 既存の全テスト: 349 passed; 17 failed (Phase 0時と同様、退行なし)
|
2025-11-22 01:59:27 +09:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 💡 Phase 3: 全体統一(Phase 22 候補)
|
|
|
|
|
|
|
|
|
|
|
|
**工数**: 10-15時間
|
|
|
|
|
|
**効果**: 完全統一達成、コード品質向上
|
|
|
|
|
|
**リスク**: 中(広範囲の変更)
|
|
|
|
|
|
|
|
|
|
|
|
### タスク
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **3.1: 素手 split 置き換え調査**
|
|
|
|
|
|
- コマンド: `rg '"\."' --type rust src/mir/builder/`
|
|
|
|
|
|
- コマンド: `rg '\.split\("\.\"\)' --type rust src/`
|
|
|
|
|
|
- リスト化: 置き換え対象箇所を列挙
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **3.2: builder/calls/unified_emitter.rs 統一**
|
|
|
|
|
|
- ファイル: `src/mir/builder/calls/unified_emitter.rs`
|
|
|
|
|
|
- CalleeResolver で StaticMethodId 使用:
|
|
|
|
|
|
```rust
|
|
|
|
|
|
use crate::mir::naming::StaticMethodId;
|
|
|
|
|
|
|
|
|
|
|
|
// methodization ブロック
|
|
|
|
|
|
if let Callee::Global(name) = callee {
|
|
|
|
|
|
if let Some(id) = StaticMethodId::parse(&name) {
|
|
|
|
|
|
// TypeRegistry で static box method か確認
|
|
|
|
|
|
if self.is_static_box_method(&id) {
|
|
|
|
|
|
// Method 化
|
|
|
|
|
|
callee = Callee::Method { ... };
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **3.3: LLVM executor 統一**
|
|
|
|
|
|
- ファイル: LLVM 関連の executor(要特定)
|
|
|
|
|
|
- VM と同じルールで名前解決
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **3.4: Hako 側の統一**
|
|
|
|
|
|
- ファイル: `lang/src/mir/builder/func_lowering.hako` など
|
|
|
|
|
|
- 素手 split を NamingBox 相当の処理に置き換え
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **3.5: テスト実行**
|
|
|
|
|
|
- 全テスト通過確認: `cargo test --release`
|
|
|
|
|
|
- スモークテスト: `tools/smokes/v2/run.sh --profile quick`
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 📚 Phase 4: ドキュメント整備(Phase 23+ 候補)
|
|
|
|
|
|
|
|
|
|
|
|
**工数**: 2-3時間
|
|
|
|
|
|
**効果**: 再発防止、開発者体験向上
|
|
|
|
|
|
**リスク**: ゼロ
|
|
|
|
|
|
|
|
|
|
|
|
### タスク
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **4.1: Phase 21.7 README 更新**
|
|
|
|
|
|
- ファイル: `docs/development/roadmap/phases/phase-21.7-normalization/README.md`
|
|
|
|
|
|
- 追記内容:
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
## Global 名の SSOT ルール
|
|
|
|
|
|
|
|
|
|
|
|
### 原則
|
|
|
|
|
|
- Global 関数名は **`Box.method/N`** が SSOT
|
|
|
|
|
|
- VM/LLVM で `Box.method` を受け取ったら、arity は `args.len()` から補完
|
|
|
|
|
|
- すべての名前解決は `NamingBox::StaticMethodId` 経由
|
|
|
|
|
|
|
|
|
|
|
|
### 実装
|
|
|
|
|
|
- **NamingBox**: `src/mir/naming.rs`
|
|
|
|
|
|
- `StaticMethodId::parse()`: 名前のパース
|
|
|
|
|
|
- `StaticMethodId::format()`: 正規化された名前生成
|
|
|
|
|
|
- `StaticMethodId::with_arity()`: arity 補完
|
|
|
|
|
|
|
|
|
|
|
|
- **VM**: `src/backend/mir_interpreter/handlers/calls/global.rs`
|
|
|
|
|
|
- `StaticMethodId` で名前解決
|
|
|
|
|
|
- arity 無し → `args.len()` で補完
|
|
|
|
|
|
|
|
|
|
|
|
- **UnifiedCallEmitter**: `src/mir/builder/calls/unified_emitter.rs`
|
|
|
|
|
|
- Methodization で `StaticMethodId` 使用
|
|
|
|
|
|
- TypeRegistry と連携して static box method 判定
|
|
|
|
|
|
|
|
|
|
|
|
### デバッグ
|
|
|
|
|
|
- `NYASH_DEBUG_FUNCTION_LOOKUP=1`: VM 関数ルックアップ詳細
|
|
|
|
|
|
- `NYASH_DEBUG_USING=1`: using 解決詳細
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **4.2: トラブルシューティングガイド作成**
|
|
|
|
|
|
- ファイル: `docs/development/troubleshooting/using-resolution.md` (新規)
|
|
|
|
|
|
- 内容:
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
# Using 解決トラブルシューティング
|
|
|
|
|
|
|
|
|
|
|
|
## エラーパターン別対処法
|
|
|
|
|
|
|
|
|
|
|
|
### 1. `[using] Module not found: 'ModuleName'`
|
|
|
|
|
|
|
|
|
|
|
|
**原因**:
|
|
|
|
|
|
- nyash.toml に alias が定義されていない
|
|
|
|
|
|
- TOML parse エラーで alias が読み込めていない
|
|
|
|
|
|
- タイポ
|
|
|
|
|
|
|
|
|
|
|
|
**対処法**:
|
|
|
|
|
|
1. TOML parse エラーを確認(上に警告が出ているはず)
|
|
|
|
|
|
2. `NYASH_DEBUG_USING=1` で利用可能な alias を確認
|
|
|
|
|
|
3. nyash.toml の [using.aliases] セクションを確認
|
|
|
|
|
|
|
|
|
|
|
|
### 2. `Function not found: Box.method`
|
|
|
|
|
|
|
|
|
|
|
|
**原因**:
|
|
|
|
|
|
- 関数が存在しない
|
|
|
|
|
|
- arity が合っていない(method vs method/2)
|
|
|
|
|
|
- using で読み込んだモジュールがコンパイルされていない
|
|
|
|
|
|
|
|
|
|
|
|
**対処法**:
|
|
|
|
|
|
1. エラーメッセージの「Did you mean:」を確認
|
|
|
|
|
|
2. `NYASH_DEBUG_FUNCTION_LOOKUP=1` で関数テーブルを確認
|
|
|
|
|
|
3. arity が必要な場合、`Box.method/N` の形式で呼ぶ
|
|
|
|
|
|
|
|
|
|
|
|
### 3. `TOML parse error`
|
|
|
|
|
|
|
|
|
|
|
|
**原因**:
|
|
|
|
|
|
- TOML 構文エラー
|
|
|
|
|
|
- キー衝突(scalar と table)
|
|
|
|
|
|
|
|
|
|
|
|
**対処法**:
|
|
|
|
|
|
1. エラーメッセージの行番号を確認
|
|
|
|
|
|
2. 同じキーが scalar と table で定義されていないか確認
|
|
|
|
|
|
3. TOML validator で検証
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **4.3: 関数ルックアップガイド作成**
|
|
|
|
|
|
- ファイル: `docs/development/troubleshooting/function-lookup.md` (新規)
|
|
|
|
|
|
- 内容:
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
# 関数ルックアップガイド
|
|
|
|
|
|
|
|
|
|
|
|
## 名前解決の流れ
|
|
|
|
|
|
|
|
|
|
|
|
1. **パース**: `StaticMethodId::parse("Box.method/2")`
|
|
|
|
|
|
→ `{ box_name: "Box", method: "method", arity: Some(2) }`
|
|
|
|
|
|
|
|
|
|
|
|
2. **正規化**: box_name を normalize(main → Main)
|
|
|
|
|
|
|
|
|
|
|
|
3. **arity 補完**: arity が None なら args.len() から補完
|
|
|
|
|
|
|
|
|
|
|
|
4. **フォーマット**: `id.format()` → "Box.method/2"
|
|
|
|
|
|
|
|
|
|
|
|
5. **ルックアップ**: 関数テーブルで検索
|
|
|
|
|
|
|
|
|
|
|
|
## デバッグ方法
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 詳細ログ有効化
|
|
|
|
|
|
NYASH_DEBUG_FUNCTION_LOOKUP=1 ./target/release/hakorune program.hako
|
|
|
|
|
|
|
|
|
|
|
|
# 出力例
|
|
|
|
|
|
[DEBUG/vm] Looking up function: 'StringUtils.starts_with'
|
|
|
|
|
|
[DEBUG/vm] Parsed: box='StringUtils', method='starts_with', arity=None
|
|
|
|
|
|
[DEBUG/vm] After arity補完: 'StringUtils.starts_with/2'
|
|
|
|
|
|
[DEBUG/vm] ✅ 'StringUtils.starts_with/2' found
|
|
|
|
|
|
```
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ✅ 完了確認
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 0 完了条件
|
|
|
|
|
|
- [ ] すべてのエラーメッセージが親切(Did you mean 提案付き)
|
|
|
|
|
|
- [ ] Silent Failure がゼロ
|
|
|
|
|
|
- [ ] デバッグ方法が明示されている
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 1 完了条件
|
|
|
|
|
|
- [ ] StaticMethodId のテストが全通過
|
|
|
|
|
|
- [ ] Round-trip テストが成功
|
|
|
|
|
|
- [ ] 既存機能に影響なし
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 2 完了条件
|
|
|
|
|
|
- [ ] VM が StaticMethodId ベースで動作
|
|
|
|
|
|
- [ ] arity 有り/無し両方で正しく動作
|
|
|
|
|
|
- [ ] 既存テスト全通過
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 3 完了条件
|
|
|
|
|
|
- [ ] 全箇所が NamingBox 経由
|
|
|
|
|
|
- [ ] 素手 split がゼロ
|
|
|
|
|
|
- [ ] スモークテスト全通過
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 4 完了条件
|
|
|
|
|
|
- [ ] ドキュメントが充実
|
|
|
|
|
|
- [ ] 次回のバグで即原因特定可能
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 📝 メモ・気づき
|
|
|
|
|
|
|
|
|
|
|
|
<!-- ここに実装中の気づきや問題点をメモ -->
|
|
|
|
|
|
|