Skip to content

Commit 6a758d9

Browse files
committed
Diagnose unsupported syntax before lowering
1 parent 8a30570 commit 6a758d9

4 files changed

Lines changed: 123 additions & 1 deletion

File tree

RSScript_v0.5_Spec.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2365,6 +2365,7 @@ take of handle field
23652365
implicit conversion attempt
23662366
operator overload attempt
23672367
feature violation
2368+
unsupported syntax
23682369
unmappable rustc diagnostic
23692370
native boundary violation
23702371
```

src/analyzer.rs

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use crate::diagnostic::{Diagnostic, code};
55
use crate::hir::{DuplicateSymbolKind, Hir, HirTypeKind};
66
use crate::lexer::{Token, lex};
77
use crate::syntax::ast::{
8-
Callee, DataEffect, EffectDecl, Expr, GenericBound, GenericParam, Item, Stmt, TypeKind, TypeRef,
8+
Block, Callee, DataEffect, EffectDecl, Expr, GenericBound, GenericParam, Item, Stmt, TypeKind,
9+
TypeRef,
910
};
1011
use crate::syntax::parse_source;
1112

@@ -90,6 +91,7 @@ impl Analyzer<'_> {
9091
fn run(&mut self) {
9192
self.check_single_feature_declaration();
9293
self.check_removed_profile_declarations();
94+
self.check_unsupported_syntax();
9395
self.check_duplicate_declarations();
9496
self.check_signature_explicitness();
9597
self.check_generic_constraints();
@@ -142,6 +144,112 @@ impl Analyzer<'_> {
142144
}
143145
}
144146

147+
fn check_unsupported_syntax(&mut self) {
148+
let bodies = self
149+
.syntax_program
150+
.items
151+
.iter()
152+
.filter_map(|item| match item {
153+
Item::Function(function) => Some(function.body.clone()),
154+
Item::Type(_) => None,
155+
})
156+
.collect::<Vec<_>>();
157+
for body in &bodies {
158+
self.check_unsupported_syntax_block(body);
159+
}
160+
}
161+
162+
fn check_unsupported_syntax_block(&mut self, block: &Block) {
163+
for statement in &block.statements {
164+
self.check_unsupported_syntax_stmt(statement);
165+
}
166+
}
167+
168+
fn check_unsupported_syntax_stmt(&mut self, statement: &Stmt) {
169+
match statement {
170+
Stmt::Let(stmt) => {
171+
if let Some(value) = &stmt.value {
172+
self.check_unsupported_syntax_expr(value);
173+
}
174+
}
175+
Stmt::Return(stmt) => {
176+
if let Some(value) = &stmt.value {
177+
self.check_unsupported_syntax_expr(value);
178+
}
179+
}
180+
Stmt::With(stmt) => {
181+
self.check_unsupported_syntax_expr(&stmt.resource);
182+
self.check_unsupported_syntax_block(&stmt.body);
183+
}
184+
Stmt::If(stmt) => {
185+
self.check_unsupported_syntax_expr(&stmt.condition);
186+
self.check_unsupported_syntax_block(&stmt.then_body);
187+
if let Some(else_body) = &stmt.else_body {
188+
self.check_unsupported_syntax_block(else_body);
189+
}
190+
}
191+
Stmt::Loop(stmt) => {
192+
if let Some(condition) = &stmt.condition {
193+
self.check_unsupported_syntax_expr(condition);
194+
}
195+
self.check_unsupported_syntax_block(&stmt.body);
196+
}
197+
Stmt::Expr(expr) => self.check_unsupported_syntax_expr(expr),
198+
Stmt::Break(_) | Stmt::Continue(_) => {}
199+
Stmt::Unknown(span) => self.unsupported_syntax(
200+
span.clone(),
201+
"unsupported statement",
202+
"This statement is outside the current RSScript parser surface.",
203+
),
204+
}
205+
}
206+
207+
fn check_unsupported_syntax_expr(&mut self, expr: &Expr) {
208+
match expr {
209+
Expr::Binary { left, right, .. } => {
210+
self.check_unsupported_syntax_expr(left);
211+
self.check_unsupported_syntax_expr(right);
212+
}
213+
Expr::Field { base, .. } => self.check_unsupported_syntax_expr(base),
214+
Expr::Index { base, index, .. } => {
215+
self.check_unsupported_syntax_expr(base);
216+
self.check_unsupported_syntax_expr(index);
217+
}
218+
Expr::Call { args, .. } => {
219+
for arg in args {
220+
self.check_unsupported_syntax_expr(&arg.value);
221+
}
222+
}
223+
Expr::Effect { value, .. } | Expr::Manage { value, .. } | Expr::Try { value, .. } => {
224+
self.check_unsupported_syntax_expr(value);
225+
}
226+
Expr::Closure { body, .. } => self.check_unsupported_syntax_block(body),
227+
Expr::Ident(_, _) | Expr::Number(_, _) | Expr::String(_, _) => {}
228+
Expr::Unknown(span) => self.unsupported_syntax(
229+
span.clone(),
230+
"unsupported expression",
231+
"This expression is outside the current RSScript parser surface.",
232+
),
233+
}
234+
}
235+
236+
fn unsupported_syntax(&mut self, span: crate::diagnostic::Span, label: &str, cause: &str) {
237+
self.diagnostics.push(
238+
Diagnostic::error(
239+
code::UNSUPPORTED_SYNTAX,
240+
"unsupported RSScript syntax.",
241+
span,
242+
label,
243+
)
244+
.with_cause(cause)
245+
.with_fix(
246+
"rewrite_supported_syntax",
247+
"Rewrite this construct using the currently supported RSScript syntax.",
248+
"manual",
249+
),
250+
);
251+
}
252+
145253
fn check_signature_explicitness(&mut self) {
146254
for item in &self.syntax_program.items {
147255
let Item::Function(function) = item else {

src/diagnostic.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod code {
1515
pub const REMOVED_RUNTIME_EFFECT: &str = "RS0012";
1616
pub const INVALID_TRY_OPERATOR: &str = "RS0013";
1717
pub const INVALID_NOALLOC_ALLOCATION: &str = "RS0014";
18+
pub const UNSUPPORTED_SYNTAX: &str = "RS0015";
1819
pub const FEATURE_VIOLATION: &str = "RS0101";
1920
pub const UNNAMED_ARGUMENT: &str = "RS0201";
2021
pub const MISSING_DATA_EFFECT: &str = "RS0202";
@@ -279,6 +280,11 @@ static DIAGNOSTIC_EXPLANATIONS: &[DiagnosticExplanation] = &[
279280
title: "invalid try operator",
280281
explanation: "`?` may only be used inside functions that return a compatible `Result<T, E>` type.",
281282
},
283+
DiagnosticExplanation {
284+
code: code::UNSUPPORTED_SYNTAX,
285+
title: "unsupported syntax",
286+
explanation: "The frontend parser could not lower this source construct into the supported RSScript AST. The checker reports this before Rust lowering so unsupported source does not become generated Rust `todo!()` code.",
287+
},
282288
DiagnosticExplanation {
283289
code: code::INVALID_NOALLOC_ALLOCATION,
284290
title: "invalid noalloc allocation",
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// expect: RS0015
2+
3+
fn bad(path: read Path) -> Unit {
4+
with File.open(path: read path) {
5+
return Unit
6+
}
7+
}

0 commit comments

Comments
 (0)