feat(linter): add no-bitwise rule (#228)

This commit is contained in:
Wenzhe Wang 2023-04-01 00:11:46 +08:00 committed by GitHub
parent 76282706b9
commit 20693dc072
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 260 additions and 0 deletions

View file

@ -132,6 +132,12 @@ impl<'a> Expression<'a> {
matches!(self, Self::UnaryExpression(expr) if expr.operator == UnaryOperator::Void)
}
/// Determines whether the given expr is a `0`
#[must_use]
pub fn is_number_0(&self) -> bool {
matches!(self, Self::NumberLiteral(lit) if lit.value == 0.0)
}
/// Determines whether the given expr evaluate to `undefined`
#[must_use]
pub fn evaluate_to_undefined(&self) -> bool {

View file

@ -25,6 +25,7 @@ oxc_macros::declare_all_lint_rules! {
no_constant_binary_expression,
no_compare_neg_zero,
no_unsafe_negation,
no_bitwise,
deepscan::uninvoked_array_callback,
use_isnan,
valid_typeof,

View file

@ -0,0 +1,157 @@
use oxc_ast::{ast::BinaryOperator, AstKind, Span};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint(no-bitwise): Unexpected use of {0:?}")]
#[diagnostic(
severity(warning),
help("bitwise operators are not allowed, maybe you mistyped `&&` or `||`")
)]
struct NoBitwiseDiagnostic(&'static str, #[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct NoBitwise {
allow: Vec<String>,
int32_hint: bool,
}
declare_oxc_lint!(
/// ### What it does
///
/// Disallow bitwise operators
///
/// ### Why is this bad?
///
/// The use of bitwise operators in JavaScript is very rare and often `&` or `|` is simply a mistyped `&&` or `||`,
/// which will lead to unexpected behavior.
///
/// ### Example
///
/// ```javascript
/// var x = y | z;
/// ```
NoBitwise,
nursery
);
impl Rule for NoBitwise {
fn from_configuration(value: serde_json::Value) -> Self {
let obj = value.get(0);
Self {
allow: obj
.and_then(|v| v.get("allow"))
.and_then(serde_json::Value::as_array)
.map(|v| {
v.iter()
.filter_map(serde_json::Value::as_str)
.map(ToString::to_string)
.collect()
})
.unwrap_or_default(),
int32_hint: obj
.and_then(|v| v.get("int32Hint"))
.and_then(serde_json::Value::as_bool)
.unwrap_or_default(),
}
}
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
match node.get().kind() {
AstKind::BinaryExpression(bin_expr) => {
let op = bin_expr.operator.as_str();
if bin_expr.operator.is_bitwise()
&& !allowed_operator(&self.allow, op)
&& !is_int32_hint(self.int32_hint, node)
{
ctx.diagnostic(NoBitwiseDiagnostic(op, bin_expr.span));
}
}
AstKind::UnaryExpression(unary_expr) => {
let op = unary_expr.operator.as_str();
if unary_expr.operator.is_bitwise()
&& !allowed_operator(&self.allow, op)
&& !is_int32_hint(self.int32_hint, node)
{
ctx.diagnostic(NoBitwiseDiagnostic(op, unary_expr.span));
}
}
AstKind::AssignmentExpression(assign_expr) => {
let op = assign_expr.operator.as_str();
if assign_expr.operator.is_bitwise()
&& !allowed_operator(&self.allow, op)
&& !is_int32_hint(self.int32_hint, node)
{
ctx.diagnostic(NoBitwiseDiagnostic(op, assign_expr.span));
}
}
_ => {}
}
}
}
fn allowed_operator(allow: &[String], operator: &str) -> bool {
allow.iter().any(|s| s == operator)
}
fn is_int32_hint(int32_hint: bool, node: &AstNode) -> bool {
if !int32_hint {
return false;
}
match node.get().kind() {
AstKind::BinaryExpression(bin_expr) => {
bin_expr.operator == BinaryOperator::BitwiseOR && bin_expr.right.is_number_0()
}
_ => false,
}
}
#[test]
fn test() {
use serde_json::json;
use crate::tester::Tester;
let pass = vec![
("a + b", None),
("!a", None),
("a && b", None),
("a || b", None),
("a += b", None),
("a &&= b", None),
("a ||= b", None),
("a ??= b", None),
("~[1, 2, 3].indexOf(1)", Some(json!([ { "allow": ["~"] }]))),
("~1<<2 === -8", Some(json!([ { "allow": ["~", "<<"] }]))),
("a|0", Some(json!([ { "int32Hint": true}]))),
("a|0", Some(json!([ { "int32Hint": false, "allow": ["|"] }]))),
];
let fail = vec![
("a ^ b", None),
("a | b", None),
("a & b", None),
("a << b", None),
("a >> b", None),
("a >>> b", None),
("~a", None),
("a ^= b", None),
("a |= b", None),
("a &= b", None),
("a <<= b", None),
("a >>= b", None),
("a >>>= b", None),
];
Tester::new(NoBitwise::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,96 @@
---
source: crates/oxc_linter/src/tester.rs
expression: no_bitwise
---
⚠ eslint(no-bitwise): Unexpected use of "^"
╭─[no_bitwise.tsx:1:1]
1 │ a ^ b
· ─────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of "|"
╭─[no_bitwise.tsx:1:1]
1 │ a | b
· ─────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of "&"
╭─[no_bitwise.tsx:1:1]
1 │ a & b
· ─────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of "<<"
╭─[no_bitwise.tsx:1:1]
1 │ a << b
· ──────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of ">>"
╭─[no_bitwise.tsx:1:1]
1 │ a >> b
· ──────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of ">>>"
╭─[no_bitwise.tsx:1:1]
1 │ a >>> b
· ───────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of "~"
╭─[no_bitwise.tsx:1:1]
1 │ ~a
· ──
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of "^="
╭─[no_bitwise.tsx:1:1]
1 │ a ^= b
· ──────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of "|="
╭─[no_bitwise.tsx:1:1]
1 │ a |= b
· ──────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of "&="
╭─[no_bitwise.tsx:1:1]
1 │ a &= b
· ──────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of "<<="
╭─[no_bitwise.tsx:1:1]
1 │ a <<= b
· ───────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of ">>="
╭─[no_bitwise.tsx:1:1]
1 │ a >>= b
· ───────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`
⚠ eslint(no-bitwise): Unexpected use of ">>>="
╭─[no_bitwise.tsx:1:1]
1 │ a >>>= b
· ────────
╰────
help: bitwise operators are not allowed, maybe you mistyped `&&` or `||`