Skip to content

Commit

Permalink
Lints for ambiguous ! on the left hand side of a bitwise operation (#…
Browse files Browse the repository at this point in the history
…265)

Puts some helpful hints in order of likelihood. Could be intended to 
be: `!(x & y)`

Fulfills
https://github.com/SpaceManiac/SpacemanDMM/projects/1#card-37357276
  • Loading branch information
ZeWaka authored May 18, 2021
1 parent a47a7d2 commit f81223b
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 1 deletion.
1 change: 1 addition & 0 deletions CONFIGURING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Raised by DreamChecker:
* `must_not_sleep` - `SpacemanDMM_should_not_sleep` directive
* `redefined_proc` - `SpacemanDMM_can_be_redefined` directive
* `ambiguous_in_lhs` - Raised on ambiguous operations on the left hand side of an `in` operation
* `ambiguous_not_bitwise` - Raised on an ambiguous `!` on the left hand side of a bitwise operation
* `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
Expand Down
19 changes: 19 additions & 0 deletions src/dreamchecker/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,12 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
Expression::BinaryOp { op, lhs, rhs } => {
let lty = self.visit_expression(location, lhs, None, local_vars);
let rty = self.visit_expression(location, rhs, None, local_vars);
match op {
BinaryOp::BitAnd |
BinaryOp::BitOr |
BinaryOp::BitXor => self.check_negated_bitwise(lhs, location, *op),
_ => {}
}
self.visit_binary(lty, rty, *op)
},
Expression::AssignOp { lhs, rhs, .. } => {
Expand Down Expand Up @@ -2051,6 +2057,19 @@ impl<'o, 's> AnalyzeProc<'o, 's> {
}
}

// checks for bitwise operations on a negated LHS
fn check_negated_bitwise(&mut self, lhs: &dm::ast::Expression, location: Location, operator: BinaryOp) {
if matches!(lhs, Expression::Base { unary, .. } if unary.contains(&UnaryOp::Not)) {
error(location, format!("Ambiguous `!` on left side of bitwise `{}` operator", operator))
.with_errortype("ambiguous_not_bitwise")
.set_severity(Severity::Warning)
.with_note(location, format!("Did you mean to put !(x {} y)?", operator))
.with_note(location, format!("Did you mean to use the logical equivalent of `{}`?", operator))
.with_note(location, "Did you mean to use `~` instead of `!`?")
.register(self.context);
}
}

fn visit_binary(&mut self, lhs: Analysis<'o>, rhs: Analysis<'o>, op: BinaryOp) -> Analysis<'o> {
//println!("visit_binary: don't know anything about {}", op);
if lhs.static_ty.is_list() {
Expand Down
25 changes: 24 additions & 1 deletion src/dreamchecker/tests/operator_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

extern crate dreamchecker as dc;

use dc::test_helpers::check_errors_match;
Expand Down Expand Up @@ -54,3 +53,27 @@ fn operator_overload() {
"##.trim();
check_errors_match(code, OP_OVERLOAD_ERRORS);
}

pub const NOT_AMBIG_BITWISE_ERRORS: &[(u32, u16, &str)] = &[
(2, 8, "Ambiguous `!` on left side of bitwise `&` operator"),
(4, 8, "Ambiguous `!` on left side of bitwise `|` operator"),
(6, 8, "Ambiguous `!` on left side of bitwise `^` operator"),
];

#[test]
fn ambigous_not_bitwise() {
let code = r##"
/proc/test()
if (!1 & 0)
return
if (!1 | 0)
return
if (!1 ^ 0)
return
if (~1 & 0)
return
if (1++ & 1)
return
"##.trim();
check_errors_match(code, NOT_AMBIG_BITWISE_ERRORS);
}

0 comments on commit f81223b

Please sign in to comment.