Phase 21.3 WIP: Hako Source Checker improvements - HC011/HC016/HC017 実装完了

主な変更:
-  HC011 (dead methods) 実装・テスト緑
-  HC016 (unused alias) 実装・テスト緑
-  HC017 (non-ascii quotes) 実装完了
- 🔧 tokenizer/parser_core 強化(AST優先ルート)
- 🛡️ plugin_guard.rs 追加(stderr専用出力)
- 📋 テストインフラ整備(run_tests.sh改善)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-08 00:46:34 +09:00
parent 647e15d12e
commit 58a6471883
39 changed files with 1435 additions and 283 deletions

View File

@ -107,11 +107,88 @@ impl MirInterpreter {
pub fn execute_module(&mut self, module: &MirModule) -> Result<Box<dyn NyashBox>, VMError> {
// Snapshot functions for call resolution
self.functions = module.functions.clone();
let func = module
.functions
.get("main")
.ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?;
let ret = self.execute_function(func)?;
// Determine entry function with sensible fallbacks
// Priority:
// 1) NYASH_ENTRY env (exact), then basename before '/' if provided (e.g., "Main.main/0" → "Main.main")
// 2) "Main.main" if present
// 3) "main" (legacy/simple scripts)
let mut candidates: Vec<String> = Vec::new();
if let Ok(e) = std::env::var("NYASH_ENTRY") {
if !e.trim().is_empty() {
candidates.push(e.trim().to_string());
}
}
candidates.push("Main.main".to_string());
candidates.push("main".to_string());
// Try candidates in order
let mut chosen: Option<&nyash_rust::mir::MirFunction> = None;
for c in &candidates {
// exact
if let Some(f) = module.functions.get(c) {
chosen = Some(f);
break;
}
// if contains '/': try name before '/'
if let Some((head, _)) = c.split_once('/') {
if let Some(f) = module.functions.get(head) {
chosen = Some(f);
break;
}
}
// if looks like "Box.method": try plain "main" as last resort only when c endswith .main
if c.ends_with(".main") {
if let Some(f) = module.functions.get("main") {
chosen = Some(f);
break;
}
}
}
let func = match chosen {
Some(f) => f,
None => {
// Build helpful error message
let mut names: Vec<&String> = module.functions.keys().collect();
names.sort();
let avail = names.into_iter().take(12).cloned().collect::<Vec<_>>().join(", ");
let tried = candidates.join(", ");
let msg = format!(
"entry function not found. searched: [{}]. available: [{}]. hint: define 'static box Main {{ method main(args){{ ... }} }}' or set NYASH_ENTRY=Name",
tried, avail
);
return Err(VMError::InvalidInstruction(msg));
}
};
// Prepare arguments if the entry takes parameters (pass script args as ArrayBox)
let ret = if func.signature.params.len() == 0 {
self.execute_function(func)?
} else {
// Build argv from NYASH_SCRIPT_ARGS_JSON (set by CLI when using `--`) or NYASH_ARGV (JSON array)
let mut argv_list: Vec<String> = Vec::new();
if let Ok(s) = std::env::var("NYASH_SCRIPT_ARGS_JSON") {
if let Ok(v) = serde_json::from_str::<Vec<String>>(&s) { argv_list = v; }
} else if let Ok(s) = std::env::var("NYASH_ARGV") {
if let Ok(v) = serde_json::from_str::<Vec<String>>(&s) { argv_list = v; }
}
// Construct ArrayBox of StringBox
let array = crate::boxes::array::ArrayBox::new();
for a in argv_list.iter() {
let sb = crate::boxes::basic::StringBox::new(a);
let _ = array.push(Box::new(sb));
}
let boxed: Box<dyn crate::box_trait::NyashBox> = Box::new(array);
let arg0 = super::vm_types::VMValue::from_nyash_box(boxed);
// Fill remaining params with Void
let mut vm_args: Vec<super::vm_types::VMValue> = Vec::new();
vm_args.push(arg0);
for _ in 1..func.signature.params.len() {
vm_args.push(super::vm_types::VMValue::Void);
}
self.exec_function_inner(func, Some(&vm_args))?
};
Ok(ret.to_nyash_box())
}