Iter 4a: ail check --json mit strukturierten Diagnostics
Neue Top-Level-API check_module(&Module) -> Vec<Diagnostic> in ailang-check, plus stabile Codes (unbound-var, type-mismatch, arity-mismatch, non-exhaustive-match, unknown-ctor-in-pattern, duplicate-def, …). CLI bekommt --json-Flag für maschinenlesbares Output, Exit 1 bei Errors. Text-Modus unverändert. E2E-Test check_json_unbound_var sichert das Format ab.
This commit is contained in:
+42
-5
@@ -42,7 +42,13 @@ enum Cmd {
|
||||
json: bool,
|
||||
},
|
||||
/// Typprüft ein Modul.
|
||||
Check { path: PathBuf },
|
||||
Check {
|
||||
path: PathBuf,
|
||||
/// Strukturierte Diagnostics als JSON-Array auf stdout.
|
||||
/// Exit-Code 1, wenn mindestens ein Error gemeldet wird.
|
||||
#[arg(long)]
|
||||
json: bool,
|
||||
},
|
||||
/// Schreibt LLVM IR (.ll) für das Modul.
|
||||
EmitIr {
|
||||
path: PathBuf,
|
||||
@@ -163,10 +169,41 @@ fn main() -> Result<()> {
|
||||
print!("{}", ailang_core::pretty::module(&one));
|
||||
}
|
||||
}
|
||||
Cmd::Check { path } => {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
let r = ailang_check::check(&m)?;
|
||||
println!("ok ({} symbols)", r.symbols.len());
|
||||
Cmd::Check { path, json } => {
|
||||
if json {
|
||||
// JSON-Modus: stdout enthält ausschließlich das Diagnostics-
|
||||
// Array. Schema-Mismatch wird als strukturiertes Diagnostic
|
||||
// emittiert (mit Code `schema-mismatch`); echte I/O-Fehler
|
||||
// propagieren als fatal, weil sie keinen Modul-bezogenen
|
||||
// Diagnostic-Kontext haben.
|
||||
let diags = match ailang_core::load_module(&path) {
|
||||
Ok(m) => ailang_check::check_module(&m),
|
||||
Err(ailang_core::Error::SchemaMismatch { expected, got }) => {
|
||||
vec![ailang_check::Diagnostic::error(
|
||||
"schema-mismatch",
|
||||
format!(
|
||||
"schema mismatch: expected {expected:?}, got {got:?}"
|
||||
),
|
||||
)
|
||||
.with_ctx(serde_json::json!({
|
||||
"expected": expected,
|
||||
"actual": got,
|
||||
}))]
|
||||
}
|
||||
Err(e) => return Err(anyhow::anyhow!(e)),
|
||||
};
|
||||
println!("{}", serde_json::to_string(&diags)?);
|
||||
if diags
|
||||
.iter()
|
||||
.any(|d| matches!(d.severity, ailang_check::Severity::Error))
|
||||
{
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
let r = ailang_check::check(&m)?;
|
||||
println!("ok ({} symbols)", r.symbols.len());
|
||||
}
|
||||
}
|
||||
Cmd::EmitIr { path, out } => {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
|
||||
@@ -63,3 +63,34 @@ fn list_sum_via_match() {
|
||||
let stdout = build_and_run("list.ail.json");
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Schützt das `--json`-Diagnostic-Format für Tooling-Konsumenten.
|
||||
/// `broken_unbound.ail.json` referenziert eine nicht-existente Variable;
|
||||
/// erwartet wird Exit-Code 1 und mindestens ein Diagnostic mit
|
||||
/// `severity == "error"` und `code == "unbound-var"`.
|
||||
#[test]
|
||||
fn check_json_unbound_var() {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let workspace = Path::new(manifest_dir).parent().unwrap().parent().unwrap();
|
||||
let src = workspace.join("examples").join("broken_unbound.ail.json");
|
||||
|
||||
let output = Command::new(ail_bin())
|
||||
.args(["check", src.to_str().unwrap(), "--json"])
|
||||
.output()
|
||||
.expect("ail check --json failed to run");
|
||||
|
||||
let code = output.status.code().expect("process terminated by signal");
|
||||
assert_eq!(code, 1, "expected exit code 1, stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||
|
||||
let stdout = String::from_utf8(output.stdout).expect("stdout utf8");
|
||||
let diags: serde_json::Value =
|
||||
serde_json::from_str(stdout.trim()).expect("stdout must be valid JSON");
|
||||
let arr = diags.as_array().expect("diagnostics must be a JSON array");
|
||||
assert!(
|
||||
arr.iter().any(|d| {
|
||||
d.get("severity").and_then(|v| v.as_str()) == Some("error")
|
||||
&& d.get("code").and_then(|v| v.as_str()) == Some("unbound-var")
|
||||
}),
|
||||
"expected at least one error diagnostic with code unbound-var; got: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user