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

@ -12,3 +12,4 @@ pub mod resolve;
pub mod exec;
pub mod core_bridge;
pub mod hako;
pub mod plugin_guard;

View File

@ -0,0 +1,89 @@
/*!
* Plugin guard utilities
*
* Centralized helper to check required plugin providers and emit
* consistent diagnostics across runner modes.
*/
/// Build the list of required provider type names.
///
/// Priority:
/// - If env `NYASH_PLUGIN_OVERRIDE_TYPES` is set, use it (comma-separated).
/// - Otherwise, return a conservative default set used in VM paths.
pub fn gather_required_providers() -> Vec<String> {
if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
let mut v: Vec<String> = list
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
v.sort();
v.dedup();
return v;
}
// Default conservative set
let mut v = vec![
"FileBox".to_string(),
"ConsoleBox".to_string(),
"ArrayBox".to_string(),
"MapBox".to_string(),
"StringBox".to_string(),
"IntegerBox".to_string(),
];
v
}
/// Return missing providers by checking the unified registry.
pub fn detect_missing_providers(required: &[String]) -> Vec<String> {
let reg = nyash_rust::runtime::get_global_registry();
let mut missing: Vec<String> = Vec::new();
for t in required {
if reg.get_provider(t).is_none() {
missing.push(t.clone());
}
}
missing
}
/// Emit hints for specific provider types.
fn emit_hints_for(missing: &[String]) {
if missing.iter().any(|t| t == "FileBox") {
eprintln!("[plugin/hint] FileBox plugin is required for file I/O (new FileBox/open/read).");
eprintln!("[plugin/hint] Build and load plugin: see tools/plugin_v2_smoke.sh or configure nyash.toml [libraries.*.FileBox].");
eprintln!("[plugin/hint] Ensure LD_LIBRARY_PATH (or platform equivalent) includes the plugin directory.");
eprintln!("[plugin/hint] For analyzer runs, you can avoid FileBox via --source-file <path> <text>.");
}
}
/// Check provider availability and emit diagnostics.
///
/// - `strict`: exit(1) when any provider is missing.
/// - `quiet_pipe`: respect quiet JSON pipelines; we still write diagnostics to stderr only.
/// - `label`: context label (e.g., "vm", "vm-fallback") for messages.
pub fn check_and_report(strict: bool, quiet_pipe: bool, label: &str) {
let required = gather_required_providers();
let missing = detect_missing_providers(&required);
if missing.is_empty() {
return;
}
if strict {
eprintln!(
"{} plugin-first strict: missing providers for: {:?}",
label, missing
);
emit_hints_for(&missing);
// Do not print anything to stdout in quiet mode; just exit with 1
std::process::exit(1);
} else {
eprintln!(
"[plugin/missing] {} providers not loaded: {:?}",
label, missing
);
emit_hints_for(&missing);
if quiet_pipe {
// In quiet JSON mode, avoid noisy stdout; hints are on stderr already.
}
}
}

View File

@ -12,6 +12,13 @@ impl NyashRunner {
// Initialize plugin host so method_id injection can resolve plugin calls
crate::runner_plugin_init::init_bid_plugins();
// Friendly plugin guard (nonstrict): unify diagnostics across modes
crate::runner::modes::common_util::plugin_guard::check_and_report(
false,
crate::config::env::env_bool("NYASH_JSON_ONLY"),
"llvm",
);
// Read the file
let code = match fs::read_to_string(filename) {
Ok(content) => content,

View File

@ -67,30 +67,13 @@ impl NyashRunner {
}
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
// Strict mode: verify providers exist for override types
if crate::config::env::env_bool("NYASH_VM_PLUGIN_STRICT") {
let v2 = nyash_rust::runtime::get_global_registry();
let mut missing: Vec<String> = Vec::new();
for t in [
"FileBox",
"ConsoleBox",
"ArrayBox",
"MapBox",
"StringBox",
"IntegerBox",
] {
if v2.get_provider(t).is_none() {
missing.push(t.to_string());
}
}
if !missing.is_empty() {
eprintln!(
"❌ VM plugin-first strict: missing providers for: {:?}",
missing
);
std::process::exit(1);
}
}
// Centralized plugin guard
let strict = crate::config::env::env_bool("NYASH_VM_PLUGIN_STRICT");
crate::runner::modes::common_util::plugin_guard::check_and_report(
strict,
quiet_pipe,
"vm",
);
}
// Read the file

View File

@ -295,6 +295,12 @@ impl NyashRunner {
// Execute via MIR interpreter
let mut vm = MirInterpreter::new();
// Centralized plugin guard (non-strict by default on fallback route)
crate::runner::modes::common_util::plugin_guard::check_and_report(
false,
crate::config::env::env_bool("NYASH_JSON_ONLY"),
"vm-fallback",
);
// Optional: verify MIR before execution (dev-only)
if crate::config::env::env_bool("NYASH_VM_VERIFY_MIR") {
let mut verifier = crate::mir::verification::MirVerifier::new();