mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(linter): add no-bitwise rule (#228)
This commit is contained in:
parent
76282706b9
commit
20693dc072
4 changed files with 260 additions and 0 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
157
crates/oxc_linter/src/rules/no_bitwise.rs
Normal file
157
crates/oxc_linter/src/rules/no_bitwise.rs
Normal 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();
|
||||
}
|
||||
96
crates/oxc_linter/src/snapshots/no_bitwise.snap
Normal file
96
crates/oxc_linter/src/snapshots/no_bitwise.snap
Normal 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 `||`
|
||||
|
||||
Loading…
Reference in a new issue