mirror of
https://github.com/SpaceManiac/SpacemanDMM.git
synced 2025-12-23 05:36:47 +00:00
Populate errortype on more errors (#201)
- Added unreachable_code, proc_has_no_parent, and loop_condition_determinate error types. - Added one use of control_condition_static where it was missing. - Added unit tests covering some of the error conditions I looked at while adding these.
This commit is contained in:
parent
ff60f2cf1a
commit
21c4192395
4 changed files with 51 additions and 1 deletions
|
|
@ -43,9 +43,12 @@ Raised by DreamChecker:
|
|||
* `no_typehint_implicit_new` - Raised on the use of `new` where no typehint is avaliable
|
||||
* `field_access_static_type` - Raised on using `.field_name` on a variable with no typehint
|
||||
* `proc_call_static_type` - Raised on using `.proc_name()` on a variable with no typehint
|
||||
* `proc_has_no_parent` - Raised on calling `..()` in a proc with no parent.
|
||||
* `no_operator_overload` - Raised on using a unary operator on a non-primative that doesn't define it's own override, eg `somemob++`
|
||||
* `unreachable_code` - Raised on finding code that can never be executed
|
||||
* `control_condition_static` - Raised on a control condition such as `if`/`while` having a static condition such as `1` or `"string"`
|
||||
* `if_condition_determinate` - Raised on if condition being always true or always false
|
||||
* `loop_condition_determinate` - Raised on loop condition such as in `for` being always true or always false
|
||||
|
||||
Raised by Lexer:
|
||||
|
||||
|
|
|
|||
|
|
@ -1143,6 +1143,7 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
|
|||
for stmt in block.iter() {
|
||||
if term.terminates() {
|
||||
error(stmt.location,"possible unreachable code here")
|
||||
.with_errortype("unreachable_code")
|
||||
.register(self.context);
|
||||
return term // stop evaluating
|
||||
}
|
||||
|
|
@ -1156,10 +1157,12 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
|
|||
match expression.is_truthy() {
|
||||
Some(true) => {
|
||||
error(location,"loop condition is always true")
|
||||
.with_errortype("loop_condition_determinate")
|
||||
.register(self.context);
|
||||
},
|
||||
Some(false) => {
|
||||
error(location,"loop condition is always false")
|
||||
.with_errortype("loop_condition_determinate")
|
||||
.register(self.context);
|
||||
}
|
||||
_ => ()
|
||||
|
|
@ -1169,7 +1172,8 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
|
|||
fn visit_control_condition(&mut self, location: Location, expression: &'o Expression) {
|
||||
if expression.is_const_eval() {
|
||||
error(location,"control flow condition is a constant evalutation")
|
||||
.register(self.context);
|
||||
.with_errortype("control_condition_static")
|
||||
.register(self.context);
|
||||
}
|
||||
else if let Some(term) = expression.as_term() {
|
||||
if term.is_static() {
|
||||
|
|
@ -1223,6 +1227,7 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
|
|||
Statement::Throw(expr) => { self.visit_expression(location, expr, None, local_vars); },
|
||||
Statement::While { condition, block } => {
|
||||
let mut scoped_locals = local_vars.clone();
|
||||
// We don't check for static/determine conditions because while(TRUE) is so common.
|
||||
self.visit_expression(location, condition, None, &mut scoped_locals);
|
||||
let mut state = self.visit_block(block, &mut scoped_locals);
|
||||
state.end_loop();
|
||||
|
|
@ -1249,6 +1254,7 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
|
|||
self.visit_control_condition(condition.location, &condition.elem);
|
||||
if alwaystrue {
|
||||
error(condition.location,"unreachable if block, preceeding if/elseif condition(s) are always true")
|
||||
.with_errortype("unreachable_code")
|
||||
.register(self.context);
|
||||
}
|
||||
self.visit_expression(condition.location, &condition.elem, None, &mut scoped_locals);
|
||||
|
|
@ -1273,6 +1279,7 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
|
|||
if alwaystrue {
|
||||
if let Some(else_expr) = else_arm.first() {
|
||||
error(else_expr.location ,"unreachable else block, preceeding if/elseif condition(s) are always true")
|
||||
.with_errortype("unreachable_code")
|
||||
.register(self.context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1668,6 +1675,7 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
|
|||
self.visit_call(location, src, proc, args, true, local_vars)
|
||||
} else {
|
||||
error(location, format!("proc has no parent: {}", self.proc_ref))
|
||||
.with_errortype("proc_has_no_parent")
|
||||
.register(self.context);
|
||||
Analysis::empty()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,3 +72,24 @@ fn do_while() {
|
|||
"##.trim();
|
||||
check_errors_match(code, DO_WHILE_ERRORS);
|
||||
}
|
||||
|
||||
pub const FOR_LOOP_CONDITION_ERRORS: &[(u32, u16, &str)] = &[
|
||||
(2, 5, "loop condition is always true"),
|
||||
(2, 5, "control flow condition is a static term"),
|
||||
(4, 5, "control flow condition is a constant evalutation"),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn for_loop_condition() {
|
||||
let code = r##"
|
||||
/proc/test()
|
||||
for(var/x = 0; 1; x++)
|
||||
break
|
||||
for(var/y = 0; 5 <= 7; y++)
|
||||
break
|
||||
for(var/z = 1; z <= 6; z++) // Legit, should have no error
|
||||
break
|
||||
return
|
||||
"##.trim();
|
||||
check_errors_match(code, FOR_LOOP_CONDITION_ERRORS);
|
||||
}
|
||||
|
|
|
|||
18
src/dreamchecker/tests/proc_tests.rs
Normal file
18
src/dreamchecker/tests/proc_tests.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
extern crate dreamchecker as dc;
|
||||
|
||||
use dc::test_helpers::check_errors_match;
|
||||
|
||||
pub const NO_PARENT_ERRORS: &[(u32, u16, &str)] = &[
|
||||
(2, 5, "proc has no parent: /mob/proc/test"),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn no_parent() {
|
||||
let code = r##"
|
||||
/mob/proc/test()
|
||||
..()
|
||||
return
|
||||
"##.trim();
|
||||
check_errors_match(code, NO_PARENT_ERRORS);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue