120 lines
4.5 KiB
Rust
120 lines
4.5 KiB
Rust
//! `PrintLn` must pick a format specifier its value actually supports.
|
|
//!
|
|
//! It always emitted `{}`, which needs Display — so wiring a collection into it produced
|
|
//! a program that could not compile (E0277), and codegen called that a success. The fix
|
|
//! is deliberately narrow: `{:?}` only for the std types where `{}` is definitely wrong,
|
|
//! so nothing that works today changes, strings included.
|
|
use flowrs_core::graph::{Graph, WireMode};
|
|
use std::process::Command;
|
|
|
|
fn compiles(src: &str) -> Result<(), String> {
|
|
let dir = std::env::temp_dir().join("flowrs_println_fmt");
|
|
std::fs::create_dir_all(dir.join("src")).unwrap();
|
|
std::fs::write(
|
|
dir.join("Cargo.toml"),
|
|
"[workspace]\n[package]\nname=\"p\"\nversion=\"0.0.0\"\nedition=\"2021\"\n",
|
|
)
|
|
.unwrap();
|
|
std::fs::write(dir.join("src/main.rs"), src).unwrap();
|
|
let out = Command::new("cargo")
|
|
.args(["check", "--quiet", "--offline", "--message-format=short"])
|
|
.current_dir(&dir)
|
|
// `CARGO_TARGET_DIR` removed so this check does not depend on ambient config.
|
|
//
|
|
// `graph_build` in flowrs-mcp had a demonstrated false pass from an inherited
|
|
// target dir: its scratch crates are all `flowrs_graph 0.0.0`, and one shared
|
|
// target made them a single build unit, so cargo found a later program already
|
|
// fresh and exited 0 without reading it. These harnesses use the same package
|
|
// identity, so the same shape is possible here.
|
|
//
|
|
// Tested, and it did NOT reproduce: priming a shared target with a passing
|
|
// harness and then running this one with its own codegen broken still failed
|
|
// correctly. This is isolation against a hazard that has bitten once nearby,
|
|
// not a fix for a bug demonstrated here.
|
|
.env_remove("CARGO_TARGET_DIR")
|
|
.output()
|
|
.unwrap();
|
|
if out.status.success() {
|
|
return Ok(());
|
|
}
|
|
let e = String::from_utf8_lossy(&out.stderr);
|
|
if e.contains("failed to download") || e.contains("no matching package") {
|
|
return Ok(()); // environment limit, not a defect
|
|
}
|
|
Err(e
|
|
.lines()
|
|
.filter(|l| l.contains("error"))
|
|
.take(4)
|
|
.collect::<Vec<_>>()
|
|
.join("\n"))
|
|
}
|
|
|
|
fn lit(g: &mut Graph, ty: &str, v: &str) -> flowrs_core::NodeId {
|
|
let n = g.add_node(ty, 0.0, 0.0);
|
|
g.nodes
|
|
.get_mut(&n)
|
|
.unwrap()
|
|
.properties
|
|
.insert("value".into(), v.into());
|
|
n
|
|
}
|
|
|
|
/// The bug: a Vec has no Display, so `{}` could never work.
|
|
#[test]
|
|
fn a_collection_printed_uses_debug_and_compiles() {
|
|
let mut g = Graph::new();
|
|
let a = lit(&mut g, "IntLiteral", "0");
|
|
let b = lit(&mut g, "IntLiteral", "4");
|
|
let r = g.add_node("Range", 0.0, 0.0);
|
|
g.add_wire(a, "value", r, "start", WireMode::Move).unwrap();
|
|
g.add_wire(b, "value", r, "end", WireMode::Move).unwrap();
|
|
let c = g.add_node("Collect", 0.0, 0.0);
|
|
g.add_wire(r, "iterator", c, "iterator", WireMode::Move)
|
|
.unwrap();
|
|
let p = g.add_node("PrintLn", 0.0, 0.0);
|
|
g.add_wire(c, "collection", p, "input", WireMode::Move)
|
|
.unwrap();
|
|
|
|
let src = g.to_rust().unwrap();
|
|
assert!(
|
|
src.contains("println!(\"{:?}\""),
|
|
"a Vec needs Debug:\n{src}"
|
|
);
|
|
if let Err(e) = compiles(&src) {
|
|
panic!("printing a collection must compile:\n{e}\n---\n{src}");
|
|
}
|
|
}
|
|
|
|
/// The other direction, and the reason this is not a blanket switch to `{:?}`: a string
|
|
/// printed with Debug gains quotes, which would change the output of every graph that
|
|
/// already works.
|
|
#[test]
|
|
fn a_string_still_prints_with_display_and_no_quotes() {
|
|
let mut g = Graph::new();
|
|
let s = lit(&mut g, "StringLiteral", "hello");
|
|
let p = g.add_node("PrintLn", 0.0, 0.0);
|
|
g.add_wire(s, "value", p, "input", WireMode::Move).unwrap();
|
|
let src = g.to_rust().unwrap();
|
|
assert!(
|
|
src.contains("println!(\"{}\""),
|
|
"a string must keep Display:\n{src}"
|
|
);
|
|
}
|
|
|
|
/// A scalar keeps Display too — the common case must be untouched.
|
|
#[test]
|
|
fn a_scalar_keeps_display() {
|
|
let mut g = Graph::new();
|
|
let n = lit(&mut g, "IntLiteral", "7");
|
|
let p = g.add_node("PrintLn", 0.0, 0.0);
|
|
g.add_wire(n, "value", p, "input", WireMode::Move).unwrap();
|
|
assert!(g.to_rust().unwrap().contains("println!(\"{}\""));
|
|
}
|
|
|
|
/// An unwired PrintLn is unchanged: nothing upstream says otherwise, so it keeps `{}`.
|
|
#[test]
|
|
fn an_unconnected_println_is_unchanged() {
|
|
let mut g = Graph::new();
|
|
g.add_node("PrintLn", 0.0, 0.0);
|
|
assert!(g.to_rust().unwrap().contains("println!(\"{}\""));
|
|
}
|