@@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
33use std:: process:: Command ;
44use std:: time:: { SystemTime , UNIX_EPOCH } ;
55
6- use rsscript:: syntax:: ast:: Item ;
6+ use rsscript:: syntax:: ast:: { EffectDecl , Item } ;
77use rsscript:: syntax:: parse_source;
88use rsscript:: {
99 ReviewMapClassification , ReviewMapFileRisk , ReviewRisk , analyze_source,
@@ -1000,8 +1000,7 @@ fn copy(path: read Path) -> Result<Unit, FileError> {
10001000#[ test]
10011001fn rust_lowering_maps_native_call_boundaries ( ) {
10021002 let source = r#"
1003- fn host_emit(message: read String) -> Unit
1004- effects(native)
1003+ native fn host_emit(message: read String) -> Unit
10051004
10061005pub fn run() -> Unit {
10071006 host_emit(message: read "host")
@@ -1016,8 +1015,8 @@ pub fn run() -> Unit {
10161015 . collect :: < Vec < _ > > ( ) ;
10171016
10181017 assert_eq ! ( native_calls. len( ) , 2 ) ;
1018+ assert ! ( native_calls. iter( ) . any( |entry| entry. source. line == 5 ) ) ;
10191019 assert ! ( native_calls. iter( ) . any( |entry| entry. source. line == 6 ) ) ;
1020- assert ! ( native_calls. iter( ) . any( |entry| entry. source. line == 7 ) ) ;
10211020}
10221021
10231022#[ test]
@@ -1605,6 +1604,29 @@ fn checksum(data: read Bytes) -> UInt64
16051604 } ) ) ;
16061605}
16071606
1607+ #[ test]
1608+ fn review_reports_native_fn_as_native_boundary ( ) {
1609+ let old_source = r#"
1610+ fn host_emit(message: read String) -> Unit
1611+ {
1612+ Log.write(message: read message)
1613+ }
1614+ "# ;
1615+ let new_source = r#"
1616+ native fn host_emit(message: read String) -> Unit
1617+ "# ;
1618+
1619+ let findings = review_sources ( "old.rss" , old_source, "new.rss" , new_source) ;
1620+ let unsafe_finding = findings
1621+ . iter ( )
1622+ . find ( |finding| finding. code == "RSR012" )
1623+ . expect ( "expected native boundary review finding" ) ;
1624+
1625+ assert_eq ! ( unsafe_finding. risk, ReviewRisk :: Unsafe ) ;
1626+ assert_eq ! ( unsafe_finding. before. as_deref( ) , Some ( "<none>" ) ) ;
1627+ assert_eq ! ( unsafe_finding. after. as_deref( ) , Some ( "native" ) ) ;
1628+ }
1629+
16081630#[ test]
16091631fn review_reports_removed_guarantees ( ) {
16101632 let old_source = r#"
@@ -1869,6 +1891,26 @@ async fn fetch(url: read Url) -> Result<fresh Bytes, NetworkError>
18691891 assert ! ( analyze_source( "net.rssi" , source) . is_empty( ) ) ;
18701892}
18711893
1894+ #[ test]
1895+ fn parser_accepts_native_function_declaration ( ) {
1896+ let source = r#"
1897+ native fn Host.emit(message: read String) -> Unit
1898+ "# ;
1899+ let program = parse_source ( "host.rssi" , source) ;
1900+
1901+ let Item :: Function ( function) = & program. items [ 0 ] else {
1902+ panic ! ( "expected native function declaration" ) ;
1903+ } ;
1904+ assert_eq ! ( function. name, "Host.emit" ) ;
1905+ assert ! (
1906+ function
1907+ . effects
1908+ . iter( )
1909+ . any( |effect| matches!( effect, EffectDecl :: Name ( name) if name == "native" ) )
1910+ ) ;
1911+ assert ! ( analyze_source( "host.rssi" , source) . is_empty( ) ) ;
1912+ }
1913+
18721914#[ test]
18731915fn checker_rejects_unknown_file_features ( ) {
18741916 let source = r#"
0 commit comments