89 lines
3.7 KiB
Rust
89 lines
3.7 KiB
Rust
use super::checked;
|
|
use dever::{
|
|
compiler::{build, emit},
|
|
syntax::source::Source,
|
|
};
|
|
|
|
#[test]
|
|
fn emission_keeps_only_called_ownership_versions() {
|
|
let text = "decorate(value: Text, loud: true) (answer: Text) { answer = value + \"!\" }\ndecorate(value: Text, loud: false) (answer: Text) { answer = value + \".\" }\nmain(value: Text, loud: Bool) (answer: Text) { answer = decorate(value + \"hello\", loud) }";
|
|
let program = checked(text).unwrap();
|
|
let source = Source::new(0, "case.dever", text.into());
|
|
let main = program
|
|
.functions
|
|
.iter()
|
|
.position(|function| function.signature.name == "main")
|
|
.unwrap();
|
|
let decorate = program
|
|
.functions
|
|
.iter()
|
|
.position(|function| function.signature.name == "decorate")
|
|
.unwrap();
|
|
let generated = emit::generate(&program, &[source], &[main].into());
|
|
assert!(generated.contains(&format!("fn function{decorate}_own0(")));
|
|
assert!(!generated.contains(&format!("fn function{decorate}(")));
|
|
}
|
|
|
|
#[test]
|
|
fn final_record_fields_keep_ownership_across_calls() {
|
|
let text = "type Packet { path: Text\n values: List<Int> }\nselected(path: \"/echo\", values: List<Int>) (answer: List<Int>) { answer = values }\nselected(path: other, values: List<Int>) (answer: List<Int>) { answer = [] }\nroute(packet: Packet) (answer: List<Int>) { answer = selected(packet.path, packet.values) }\nmain() (answer: List<Int>) { packet = Packet { path = \"/echo\", values = [1, 2, 3] }\n answer = route(packet) }";
|
|
let program = checked(text).unwrap();
|
|
let source = Source::new(0, "case.dever", text.into());
|
|
let index = |name| {
|
|
program
|
|
.functions
|
|
.iter()
|
|
.position(|function| function.signature.name == name)
|
|
.unwrap()
|
|
};
|
|
let generated = emit::generate(&program, &[source], &[index("main")].into());
|
|
let selected = index("selected");
|
|
assert!(generated.contains(&format!("fn function{selected}_own0_own1(")));
|
|
assert!(!generated.contains(&format!("fn function{selected}(")));
|
|
}
|
|
|
|
#[test]
|
|
fn emitted_types_follow_entry_variables_errors_and_nested_fields() {
|
|
let text = "type Leaf { value: Channel<Int>? }\ntype Box { leaves: List<Leaf> }\ntype Unused { value: AsyncStream<Int> }\nmake() (value: Box) { value = Box { leaves = [] } }\nmain() () { value = make() }\nplain() (answer: Int) { answer = 42 }";
|
|
let program = checked(text).unwrap();
|
|
let source = Source::new(0, "case.dever", text.into());
|
|
for (entry, required) in [("main", true), ("plain", false)] {
|
|
let index = program
|
|
.functions
|
|
.iter()
|
|
.position(|function| function.signature.name == entry)
|
|
.unwrap();
|
|
let functions = program.reachable([index]);
|
|
assert_eq!(
|
|
build::runtime_features(&program, &functions).contains(&"task"),
|
|
required
|
|
);
|
|
let generated = emit::generate(&program, std::slice::from_ref(&source), &functions);
|
|
for (index, definition) in program.definitions.iter().enumerate() {
|
|
let wanted = required && matches!(definition.name.as_str(), "Leaf" | "Box");
|
|
assert_eq!(
|
|
generated.contains(&format!("pub struct Type{index} {{")),
|
|
wanted,
|
|
"{entry}: {}",
|
|
definition.name
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn unused_types_are_still_validated_before_emission() {
|
|
assert_eq!(
|
|
checked("type Unused { values: Channel<Stream<Int>> }\nmain() () {}")
|
|
.unwrap_err()
|
|
.code,
|
|
"C013"
|
|
);
|
|
assert_eq!(
|
|
checked("type Unused { next: Unused? }\nmain() () {}")
|
|
.unwrap_err()
|
|
.code,
|
|
"C007"
|
|
);
|
|
}
|