Skip to content

Commit 1a81c91

Browse files
Haofeiclaude
andcommitted
test: split checker_lowering into spec-tagged submodules (no logic change)
212 tests relocated into tests/checker_lowering/{async_concurrency,native,resources, managed,control_flow,effects,types,stdlib,source_map,misc}.rs, each spec-headed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9ed97e9 commit 1a81c91

11 files changed

Lines changed: 7120 additions & 7070 deletions

File tree

crates/rsscript/tests/checker_lowering.rs

Lines changed: 31 additions & 7070 deletions
Large diffs are not rendered by default.

crates/rsscript/tests/checker_lowering/async_concurrency.rs

Lines changed: 996 additions & 0 deletions
Large diffs are not rendered by default.

crates/rsscript/tests/checker_lowering/control_flow.rs

Lines changed: 695 additions & 0 deletions
Large diffs are not rendered by default.

crates/rsscript/tests/checker_lowering/effects.rs

Lines changed: 485 additions & 0 deletions
Large diffs are not rendered by default.

crates/rsscript/tests/checker_lowering/managed.rs

Lines changed: 650 additions & 0 deletions
Large diffs are not rendered by default.

crates/rsscript/tests/checker_lowering/misc.rs

Lines changed: 759 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
//! Spec §3/§9 — native boundary lowering and bindings
2+
#![allow(unused_imports, dead_code)]
3+
use super::*;
4+
5+
#[test]
6+
fn rust_lowering_maps_native_call_boundaries() {
7+
let source = r#"
8+
features: native
9+
10+
native fn host_emit(message: read String) -> Unit
11+
effects(native)
12+
13+
pub fn run() -> Unit {
14+
host_emit(message: read "host")
15+
Log.write(message: read "core")
16+
}
17+
"#;
18+
let package = lower_sources_to_rust_package_with_options(
19+
&[("native.rss".to_string(), source.to_string())],
20+
"Native Example.rss",
21+
"/workspace/rsscript/runtime",
22+
&[],
23+
&[NativeRustDependency {
24+
crate_name: "host_native".to_string(),
25+
path: "/workspace/host-native".to_string(),
26+
cargo_features: Vec::new(),
27+
bindings: BTreeMap::from([("host_emit".to_string(), "host_native::emit".to_string())]),
28+
}],
29+
)
30+
.expect("source should lower with native binding");
31+
let source_map: Vec<rsscript::RustSourceMapEntry> =
32+
serde_json::from_str(&package.source_map_json).expect("source map should parse");
33+
let native_calls = source_map
34+
.iter()
35+
.filter(|entry| entry.kind == "native_call")
36+
.collect::<Vec<_>>();
37+
38+
assert_eq!(native_calls.len(), 2);
39+
assert!(native_calls.iter().any(|entry| entry.source.line == 8));
40+
assert!(native_calls.iter().any(|entry| entry.source.line == 9));
41+
assert!(
42+
package
43+
.lib_rs
44+
.contains("host_native::emit(&(\"host\".to_string()));")
45+
);
46+
}
47+
48+
#[test]
49+
fn rust_lowering_maps_receiver_native_binding_with_receiver_argument() {
50+
let source = r#"
51+
features: native
52+
53+
opaque struct Alpha
54+
55+
native fn Alpha.open() -> Alpha
56+
effects(native)
57+
58+
native fn Alpha.describe(self: read Alpha) -> String
59+
effects(native)
60+
61+
pub fn run() -> Unit {
62+
let alpha = Alpha.open()
63+
Log.write(message: read alpha.describe())
64+
}
65+
"#;
66+
let package = lower_sources_to_rust_package_with_options(
67+
&[("receiver-native.rss".to_string(), source.to_string())],
68+
"Receiver Native Example.rss",
69+
"/workspace/rsscript/runtime",
70+
&[],
71+
&[NativeRustDependency {
72+
crate_name: "alpha_native".to_string(),
73+
path: "/workspace/alpha-native".to_string(),
74+
cargo_features: Vec::new(),
75+
bindings: BTreeMap::from([
76+
("Alpha.open".to_string(), "alpha_native::open".to_string()),
77+
(
78+
"Alpha.describe".to_string(),
79+
"alpha_native::describe".to_string(),
80+
),
81+
]),
82+
}],
83+
)
84+
.expect("source should lower with receiver native binding");
85+
86+
assert!(
87+
package.lib_rs.contains("alpha_native::open()"),
88+
"qualified native call should use bound target, got:\n{}",
89+
package.lib_rs
90+
);
91+
assert!(
92+
package.lib_rs.contains("alpha_native::describe(&alpha)"),
93+
"receiver native call should pass receiver as first argument to bound target, got:\n{}",
94+
package.lib_rs
95+
);
96+
assert!(
97+
!package.lib_rs.contains("Alpha::describe(&alpha)"),
98+
"receiver native binding should not fall back to generated qualified call, got:\n{}",
99+
package.lib_rs
100+
);
101+
}
102+
103+
#[test]
104+
fn rust_lowering_can_emit_native_wrapper_path_dependencies() {
105+
let source = r#"
106+
fn main() -> Unit {
107+
return Unit
108+
}
109+
"#;
110+
let package = lower_sources_to_rust_package_with_options(
111+
&[("main.rss".to_string(), source.to_string())],
112+
"Native Example.rss",
113+
"/workspace/rsscript/runtime",
114+
&[],
115+
&[NativeRustDependency {
116+
crate_name: "rss_json_native".to_string(),
117+
path: "/workspace/rss-json/native/rust".to_string(),
118+
cargo_features: Vec::new(),
119+
bindings: BTreeMap::new(),
120+
}],
121+
)
122+
.expect("source should lower into package with native dependency");
123+
124+
assert!(
125+
package
126+
.cargo_toml
127+
.contains("\"rss_json_native\" = { path = \"/workspace/rss-json/native/rust\" }")
128+
);
129+
}
130+
131+
#[test]
132+
fn checker_reports_malformed_bindings_and_arguments_as_unsupported() {
133+
let source = r#"
134+
fn main() -> Unit {
135+
let missing =
136+
print(value:)
137+
}
138+
"#;
139+
let diagnostics = analyze_source("malformed-body.rss", source);
140+
141+
assert!(
142+
diagnostics
143+
.iter()
144+
.any(|diagnostic| diagnostic.code == "RS0015"
145+
&& diagnostic.label == "malformed statement")
146+
);
147+
assert!(
148+
diagnostics
149+
.iter()
150+
.any(|diagnostic| diagnostic.code == "RS0015"
151+
&& diagnostic.label == "unsupported expression")
152+
);
153+
}
154+
155+
#[test]
156+
fn review_reports_new_unsafe_native_usage() {
157+
let old_source = r#"
158+
159+
fn checksum(data: read Bytes) -> UInt64
160+
effects(no_panic)
161+
{
162+
Bytes.checksum(data: read data)
163+
}
164+
"#;
165+
let new_source = r#"
166+
167+
fn checksum(data: read Bytes) -> UInt64
168+
effects(no_panic, unsafe, native)
169+
{
170+
Native.checksum(data: read data)
171+
}
172+
"#;
173+
174+
let findings = review_sources("old.rss", old_source, "new.rss", new_source);
175+
let unsafe_finding = findings
176+
.iter()
177+
.find(|finding| finding.code == "RSR012")
178+
.expect("expected unsafe review finding");
179+
let native_finding = findings
180+
.iter()
181+
.find(|finding| finding.code == "RSR015")
182+
.expect("expected native review finding");
183+
184+
assert_eq!(unsafe_finding.risk, ReviewRisk::Unsafe);
185+
assert_eq!(unsafe_finding.before.as_deref(), Some("<none>"));
186+
assert_eq!(unsafe_finding.after.as_deref(), Some("unsafe"));
187+
assert_eq!(native_finding.risk, ReviewRisk::Boundary);
188+
assert_eq!(native_finding.before.as_deref(), Some("<none>"));
189+
assert_eq!(native_finding.after.as_deref(), Some("native"));
190+
assert!(
191+
format_review_human(&findings)
192+
.contains("RSR012[unsafe]: function `checksum` added unsafe boundary.")
193+
);
194+
assert!(
195+
format_review_human(&findings)
196+
.contains("RSR015[boundary]: function `checksum` added native boundary.")
197+
);
198+
199+
let json = format_review_json(&findings);
200+
let value: Value = serde_json::from_str(&json).expect("review JSON should parse");
201+
assert!(value.as_array().is_some_and(|items| {
202+
items
203+
.iter()
204+
.any(|item| item["code"] == "RSR012" && item["risk"] == "unsafe")
205+
}));
206+
assert!(value.as_array().is_some_and(|items| {
207+
items
208+
.iter()
209+
.any(|item| item["code"] == "RSR015" && item["risk"] == "boundary")
210+
}));
211+
}
212+
213+
#[test]
214+
fn review_reports_native_fn_as_native_boundary() {
215+
let old_source = r#"
216+
fn host_emit(message: read String) -> Unit
217+
{
218+
Log.write(message: read message)
219+
}
220+
"#;
221+
let new_source = r#"
222+
native fn host_emit(message: read String) -> Unit
223+
effects(native)
224+
"#;
225+
226+
let findings = review_sources("old.rss", old_source, "new.rss", new_source);
227+
let native_finding = findings
228+
.iter()
229+
.find(|finding| finding.code == "RSR015")
230+
.expect("expected native boundary review finding");
231+
232+
assert_eq!(native_finding.risk, ReviewRisk::Boundary);
233+
assert_eq!(native_finding.before.as_deref(), Some("<none>"));
234+
assert_eq!(native_finding.after.as_deref(), Some("native"));
235+
assert!(!findings.iter().any(|finding| finding.code == "RSR012"));
236+
}
237+
238+
#[test]
239+
fn review_map_marks_native_calls_inside_noescape_callbacks() {
240+
let source = r#"
241+
features: native
242+
243+
native fn Native.echo(message: read String) -> String
244+
effects(native)
245+
246+
fn apply(callback: noescape Fn()) -> Unit {
247+
callback()
248+
return Unit
249+
}
250+
251+
fn caller(message: read String) -> Unit {
252+
apply(callback: || {
253+
Native.echo(message: read message)
254+
})
255+
return Unit
256+
}
257+
"#;
258+
let map = review_map_sources(vec![("callback-native.rss", source)]);
259+
let region = map.files[0]
260+
.regions
261+
.iter()
262+
.find(|region| region.function == "caller")
263+
.expect("expected caller region");
264+
265+
assert_eq!(
266+
region.classification,
267+
ReviewMapClassification::ReviewRequired
268+
);
269+
assert!(
270+
region
271+
.reasons
272+
.iter()
273+
.any(|reason| reason == "native call `Native.echo`"),
274+
"{region:?}"
275+
);
276+
}
277+
278+
#[test]
279+
fn review_map_selfhost_classifier_has_no_unknown_regions() {
280+
let path = Path::new("tests/fixtures/pass/selfhost-review-classifier.rss");
281+
let source = common::read_fixture(path);
282+
let map = review_map_sources(vec![(path.to_str().unwrap(), source.as_str())]);
283+
284+
assert!(map.summary.total_functions >= 40, "{map:?}");
285+
assert!(map.summary.total_lines >= 622, "{map:?}");
286+
assert_eq!(map.files[0].risk, ReviewMapFileRisk::Elevated);
287+
assert_eq!(map.summary.unknown.functions, 0, "{map:?}");
288+
assert_eq!(map.summary.unknown.lines, 0, "{map:?}");
289+
assert!(map.summary.review_required.functions >= 27, "{map:?}");
290+
291+
let json: Value =
292+
serde_json::from_str(&format_review_map_json(&map)).expect("review map JSON should parse");
293+
assert_eq!(json["summary"]["unknown_ratio"], 0.0);
294+
assert_eq!(json["summary"]["unknown_function_ratio"], 0.0);
295+
assert!(
296+
json["summary"]["must_review"]["functions"]
297+
.as_u64()
298+
.is_some_and(|count| count >= 27)
299+
);
300+
assert!(
301+
json["summary"]["low_semantic_risk"]["functions"]
302+
.as_u64()
303+
.is_some_and(|count| count >= 13)
304+
);
305+
}
306+
307+
#[test]
308+
fn parser_expands_native_module_declarations_to_native_functions() {
309+
let source = r#"
310+
features: native
311+
312+
struct Path
313+
resource File
314+
struct IOError
315+
316+
native module File {
317+
fn open(path: read Path) -> Result<File, IOError>
318+
}
319+
"#;
320+
let diagnostics = analyze_source("native-module.rss", source);
321+
assert_eq!(diagnostics, Vec::new());
322+
323+
let map = review_map_sources(vec![("native-module.rss", source)]);
324+
assert!(map.files[0].regions.iter().any(|region| {
325+
region
326+
.reasons
327+
.iter()
328+
.any(|reason| reason == "native boundary")
329+
}));
330+
}

0 commit comments

Comments
 (0)