Skip to content

Commit

Permalink
Populate errortype on more errors (#201)
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
Leshana authored Oct 7, 2020
1 parent ff60f2c commit 21c4192
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CONFIGURING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
10 changes: 9 additions & 1 deletion src/dreamchecker/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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);
}
_ => ()
Expand All @@ -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() {
Expand Down Expand Up @@ -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();
Expand All @@ -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);
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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()
}
Expand Down
21 changes: 21 additions & 0 deletions src/dreamchecker/tests/branch_eval_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 18 additions & 0 deletions src/dreamchecker/tests/proc_tests.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 21c4192

Please sign in to comment.