Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+156
View File
@@ -0,0 +1,156 @@
use super::checked;
use dever::compiler::{check, model::Program};
use dever::syntax::{diagnostic::Diagnostic, source::Source};
pub(super) fn packages(files: &[(&str, &str)]) -> Result<Program, Diagnostic> {
let mut modules = Vec::new();
let mut identities = Vec::new();
for (index, (identity, text)) in files.iter().enumerate() {
let source = Source::new(index, format!("{identity}.dever"), (*text).into());
modules.push(dever::syntax::parse(&source)?);
identities.push((*identity).into());
}
check::check(modules, &identities)
}
#[test]
fn public_signatures_cannot_publish_private_types() {
for declaration in [
"public echo(value: Hidden) (answer: Hidden) { answer = value }",
"public echo(value: List<Hidden?>) (answer: List<Hidden?>) { answer = value }",
"public echo(value: Map<Text, List<Hidden>>) (answer: Map<Text, List<Hidden>>) { answer = value }",
"public echo(value: MapEntry<Text, Hidden>) (answer: MapEntry<Text, Hidden>) { answer = value }",
"public apply(action: handler(value: Hidden) (answer: Hidden)) () {}",
"public type Visible { nested: Hidden }",
"public type Visible { Wrapped(value: Hidden) }",
] {
let text = format!("type Hidden {{ value: Int }}\n{declaration}");
assert_eq!(checked(&text).unwrap_err().code, "C006", "{text}");
}
assert!(checked("public type Visible { value: Int }\npublic echo(value: List<Visible?>) (answer: List<Visible?>) { answer = value }").is_ok());
}
#[test]
fn public_failures_cannot_leak_private_error_identity() {
for call in ["fail(Hidden.Failed(\"private\"))", "hidden()"] {
let text = format!(
"type Hidden {{ error Failed(message: Text) }}\nhidden() () {{ fail(Hidden.Failed(\"private\")) }}\npublic exposed() () {{ {call} }}"
);
let error = checked(&text).unwrap_err();
assert_eq!(error.code, "C006", "{error:?}");
}
assert!(checked("public type Problem { error Failed(message: Text) }\nprivateFailure() () { fail(Problem.Failed(\"public\")) }\npublic exposed() () { privateFailure() }").is_ok());
assert_eq!(
checked("public apply(action: handler(value: Map<Float, Int>) ()) () {}")
.unwrap_err()
.code,
"C005"
);
}
#[test]
fn package_cycles_are_rejected_without_rejecting_static_callbacks() {
for files in [
[
(
"left",
"public type A { value: Int }\npublic make() () { local: right.B? = null }",
),
(
"right",
"public type B { value: Int }\npublic make() () { local: left.A? = null }",
),
],
[
(
"left",
"public first() (answer: Int) { answer = right.second() }\npublic last() (answer: Int) { answer = 1 }",
),
(
"right",
"public second() (answer: Int) { answer = left.last() }",
),
],
[
(
"left",
"public type A { value: Int }\npublic fromRight(value: right.B) () {}",
),
(
"right",
"public type B { value: Int }\npublic fromLeft(value: left.A) () {}",
),
],
[
(
"left",
"public type A { value: Int }\npublic make() () { local = right.B { value = 1 } }",
),
(
"right",
"public type B { value: Int }\npublic make() () { local = left.A { value = 1 } }",
),
],
] {
assert_eq!(packages(&files).unwrap_err().code, "C007");
}
let files = [
(
"client",
"privateDouble(value: Int) (answer: Int) { answer = value * 2 }\nmain() (answer: Int) { answer = helper.apply(privateDouble, 4) }",
),
(
"helper",
"public apply(action: handler(value: Int) (answer: Int), value: Int) (answer: Int) { answer = action(value) }",
),
];
assert!(packages(&files).is_ok());
}
#[test]
fn cross_package_names_and_callbacks_obey_visibility() {
let helper = "type Hidden { value: Int }\nprivateRead(value: Int) (answer: Int) { answer = value }\npublic type Visible { value: Int }\npublic read(value: Int) (answer: Int) { answer = privateRead(value) }";
for caller in [
"main() (answer: Int) { answer = helper.privateRead(1) }",
"main() (answer: List<Int>) { answer = each(helper.privateRead, [1]) }",
"main(value: helper.Hidden) () {}",
"main() () { value = helper.Hidden { value = 1 } }",
] {
assert_eq!(
packages(&[("main", caller), ("helper", helper)])
.unwrap_err()
.code,
"C006"
);
}
assert!(packages(&[("main", "main() (answer: helper.Visible) { answer = helper.Visible { value = helper.read(3) } }"), ("helper", helper)]).is_ok());
}
#[test]
fn private_fields_reject_foreign_reads_writes_and_construction() {
let vault = "public type Session { private key: Text\nlabel: Text }\npublic type Envelope { value: Session }";
for caller in [
"read(value: vault.Session) (answer: Text) { answer = value.key }",
"write(value: vault.Session) () { value.key = \"changed\" }",
"make() (answer: vault.Session) { answer = vault.Session { key = \"hidden\", label = \"name\" } }",
"make() (answer: vault.Session) { answer = vault.Session { label = \"name\" } }",
"read(value: vault.Envelope) (answer: Text) { answer = value.value.key }",
"write(value: vault.Envelope) () { value.value.key = \"changed\" }",
] {
let error = packages(&[("main", caller), ("vault", vault)]).unwrap_err();
assert_eq!(error.code, "C006", "{caller}: {error:?}");
assert!(error.message.contains("field is private"), "{error:?}");
}
assert!(packages(&[("main", "rename(value: vault.Session) (answer: vault.Session) { value.label = \"new\"\nanswer = value }"), ("vault", vault)]).is_ok());
}
#[test]
fn private_record_fields_can_hide_types_but_choice_payloads_cannot() {
assert!(checked("type Hidden { value: Int }\npublic type Visible { private hidden: Hidden\nlabel: Text }\npublic make() (answer: Visible) { answer = Visible { hidden = Hidden { value = 7 }, label = \"name\" } }\npublic read(value: Visible) (answer: Int) { answer = value.hidden.value }").is_ok());
assert_eq!(
checked("public type Choice { Ready(private value: Int) }")
.unwrap_err()
.code,
"C005"
);
}