use std::process::Command;
use object::{Object, ObjectSymbol};
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn fixture() -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("scripts/repro")
.join("call_indirect_275_selfcontained.wat")
}
fn has_symbol(bytes: &[u8], name: &str) -> bool {
let obj = object::File::parse(bytes).expect("parse object");
obj.symbols().any(|s| s.name() == Ok(name))
}
#[test]
fn test_275_selfcontained_call_indirect_declines_loudly() {
let path = fixture();
let elf = "/tmp/ci275_selfcontained.elf";
let out = Command::new(synth())
.args([
"compile",
path.to_str().unwrap(),
"--cortex-m",
"--target",
"cortex-m3",
"-o",
elf,
])
.output()
.expect("run synth");
assert!(
out.status.success(),
"compile should skip-and-continue: {}",
String::from_utf8_lossy(&out.stderr)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("skipping function 'entry'"),
"the call_indirect function must be a LOUD skip, got:\n{stderr}"
);
assert!(
stderr.contains("#275"),
"the decline diagnostic must name #275, got:\n{stderr}"
);
assert!(
stderr.contains("were skipped"),
"the skip must be surfaced in the summary count, got:\n{stderr}"
);
let bytes = std::fs::read(elf).expect("read elf");
assert!(
!has_symbol(&bytes, "entry"),
"`entry` must NOT be emitted — a declined function is absent, not a \
silent colliding dispatch (#275)"
);
for f in ["func_1", "func_2", "func_3"] {
assert!(
has_symbol(&bytes, f),
"{f} must still compile — only the call_indirect function declines"
);
}
}
#[test]
fn test_275_relocatable_call_indirect_untouched() {
let path = fixture();
for target in ["cortex-m3", "cortex-r5"] {
let elf = format!("/tmp/ci275_reloc_{target}.o");
let out = Command::new(synth())
.args([
"compile",
path.to_str().unwrap(),
"--target",
target,
"--all-exports",
"--relocatable",
"--no-optimize",
"-o",
&elf,
])
.output()
.expect("run synth");
assert!(
out.status.success(),
"relocatable compile failed ({target}): {}",
String::from_utf8_lossy(&out.stderr)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
!stderr.contains("skipping function 'entry'"),
"relocatable path must NOT decline entry ({target}):\n{stderr}"
);
let bytes = std::fs::read(&elf).expect("read object");
assert!(
has_symbol(&bytes, "entry"),
"relocatable path must still emit `entry` with its dispatch ({target}) — #275 \
must not touch the host-linked path"
);
}
}