Skip to content

Commit d90e0de

Browse files
committed
Reject unsupported ownership surface syntax
1 parent 984ec0b commit d90e0de

6 files changed

Lines changed: 102 additions & 0 deletions

File tree

src/analyzer.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ impl Analyzer<'_> {
163163
if param.effect.is_none()
164164
&& !param.ty.name.is_empty()
165165
&& param.ty.name != "share"
166+
&& !type_ref_has_surface_reference(&param.ty, self.tokens)
166167
&& !is_copy_type(&param.ty)
167168
{
168169
self.diagnostics.push(
@@ -747,3 +748,12 @@ fn type_ref_name(ty: &TypeRef) -> String {
747748
.join(", ");
748749
format!("{}<{args}>", ty.name)
749750
}
751+
752+
fn type_ref_has_surface_reference(ty: &TypeRef, tokens: &[Token]) -> bool {
753+
tokens.iter().any(|token| {
754+
token.symbol("&")
755+
&& token.span.file == ty.span.file
756+
&& token.span.line == ty.span.line
757+
&& token.span.column + token.span.length <= ty.span.column
758+
})
759+
}

src/checks/forbidden.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,64 @@ use crate::lexer::TokenKind;
55
pub(crate) fn check(analyzer: &mut Analyzer<'_>) {
66
check_operator_overload_attempts(analyzer);
77
check_implicit_conversion_attempts(analyzer);
8+
check_own_struct_attempts(analyzer);
9+
check_surface_reference_attempts(analyzer);
10+
}
11+
12+
fn check_own_struct_attempts(analyzer: &mut Analyzer<'_>) {
13+
for index in 0..analyzer.tokens.len().saturating_sub(1) {
14+
if analyzer.tokens[index].is_ident_text("own")
15+
&& analyzer.tokens[index + 1].is_ident_text("struct")
16+
{
17+
analyzer.diagnostics.push(
18+
Diagnostic::error(
19+
code::OWN_STRUCT_ATTEMPT,
20+
"`own struct` is not part of RSScript v0.4.1.",
21+
analyzer.tokens[index].span.clone(),
22+
"own struct attempt",
23+
)
24+
.with_cause("v0.4.1 has only `class`, `struct`, and `resource` type declarations.")
25+
.with_fix(
26+
"choose_type_kind",
27+
"Use `struct` for inline values, `class` for managed identity, or `resource` for deterministic cleanup.",
28+
"manual",
29+
),
30+
);
31+
}
32+
}
33+
}
34+
35+
fn check_surface_reference_attempts(analyzer: &mut Analyzer<'_>) {
36+
for index in 0..analyzer.tokens.len() {
37+
if !analyzer.tokens[index].symbol("&") || is_boolean_and(analyzer, index) {
38+
continue;
39+
}
40+
analyzer.diagnostics.push(
41+
Diagnostic::error(
42+
code::SURFACE_REFERENCE_ATTEMPT,
43+
"surface reference syntax is not part of RSScript.",
44+
analyzer.tokens[index].span.clone(),
45+
"surface reference attempt",
46+
)
47+
.with_cause("RSScript uses explicit data effects instead of `&T` or `&mut T` syntax.")
48+
.with_fix(
49+
"use_data_effect",
50+
"Use a parameter effect such as `value: read T` or `value: mut T`.",
51+
"manual",
52+
),
53+
);
54+
}
55+
}
56+
57+
fn is_boolean_and(analyzer: &Analyzer<'_>, index: usize) -> bool {
58+
analyzer
59+
.tokens
60+
.get(index.wrapping_sub(1))
61+
.is_some_and(|token| token.symbol("&"))
62+
|| analyzer
63+
.tokens
64+
.get(index + 1)
65+
.is_some_and(|token| token.symbol("&"))
866
}
967

1068
fn check_implicit_conversion_attempts(analyzer: &mut Analyzer<'_>) {

src/diagnostic.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub mod code {
3535
pub const TAKE_HANDLE_FIELD: &str = "RS0901";
3636
pub const OPERATOR_OVERLOAD_ATTEMPT: &str = "RS1001";
3737
pub const IMPLICIT_CONVERSION_ATTEMPT: &str = "RS1002";
38+
pub const OWN_STRUCT_ATTEMPT: &str = "RS1003";
39+
pub const SURFACE_REFERENCE_ATTEMPT: &str = "RS1004";
3840

3941
pub const REVIEW_MODE_CHANGED: &str = "RSR001";
4042
pub const REVIEW_FUNCTION_REMOVED: &str = "RSR002";
@@ -371,6 +373,16 @@ static DIAGNOSTIC_EXPLANATIONS: &[DiagnosticExplanation] = &[
371373
title: "implicit conversion attempt",
372374
explanation: "RSScript rejects cast-style conversion syntax. Conversions must be visible named APIs such as `Type.from(value: read x)`.",
373375
},
376+
DiagnosticExplanation {
377+
code: code::OWN_STRUCT_ATTEMPT,
378+
title: "own struct attempt",
379+
explanation: "RSScript v0.4.1 has exactly three type declaration kinds: `class`, `struct`, and `resource`. There is no `own struct`.",
380+
},
381+
DiagnosticExplanation {
382+
code: code::SURFACE_REFERENCE_ATTEMPT,
383+
title: "surface reference attempt",
384+
explanation: "RSScript does not expose `&T` or `&mut T` syntax. Use explicit parameter effects such as `read`, `mut`, and `take`.",
385+
},
374386
];
375387

376388
pub fn format_diagnostics_json(diagnostics: &[Diagnostic]) -> String {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// expect: RS1003
2+
mode: managed
3+
4+
own struct Buffer {
5+
bytes: Bytes
6+
}
7+
8+
fn make_buffer(bytes: read Bytes) -> fresh Buffer {
9+
return Buffer(bytes: read bytes)
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// expect: RS1004
2+
mode: managed
3+
4+
fn clear(buffer: &mut Buffer) -> Unit {
5+
Buffer.clear(buffer: mut buffer)
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// expect: RS1004
2+
mode: managed
3+
4+
fn hash(data: &Bytes) -> UInt64 {
5+
return Bytes.hash(data: read data)
6+
}

0 commit comments

Comments
 (0)