feat(linter): eslint: no-void (#2162)

Rule Detail:
[link](https://github.com/eslint/eslint/blob/main/lib/rules/no-void.js)
This commit is contained in:
cin 2024-01-25 12:40:30 +08:00 committed by GitHub
parent a5d7c67982
commit 38914308e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 147 additions and 0 deletions

View file

@ -91,6 +91,7 @@ mod eslint {
pub mod no_useless_catch;
pub mod no_useless_escape;
pub mod no_var;
pub mod no_void;
pub mod require_yield;
pub mod use_isnan;
pub mod valid_typeof;
@ -379,6 +380,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::no_useless_catch,
eslint::no_useless_escape,
eslint::no_var,
eslint::no_void,
eslint::require_yield,
eslint::use_isnan,
eslint::valid_typeof,

View file

@ -0,0 +1,97 @@
use crate::{context::LintContext, rule::Rule, AstNode};
use oxc_ast::AstKind;
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use oxc_syntax::operator::UnaryOperator;
#[derive(Debug, Error, Diagnostic)]
#[error("eslint(no-void): Disallow `void` operators")]
#[diagnostic(severity(warning), help("Expected 'undefined' and instead saw 'void'."))]
struct NoVoidDiagnostic(#[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct NoVoid {
pub allow_as_statement: bool,
}
declare_oxc_lint!(
/// ### What it does
///
/// Disallow `void` operators.
///
/// ### Example
///
/// ```javascript
/// // error
/// void 0;
/// var foo = void 0;
///
/// // success
/// "var foo = bar()",
/// "foo.void()",
/// "foo.void = bar",
/// ```
NoVoid,
restriction,
);
impl Rule for NoVoid {
fn from_configuration(value: serde_json::Value) -> Self {
let allow_as_statement = value
.get(0)
.and_then(|config| config.get("allowAsStatement"))
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
Self { allow_as_statement }
}
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::UnaryExpression(unary_expr) = node.kind() else {
return;
};
if let Some(kind) = ctx.nodes().parent_kind(node.id()) {
if self.allow_as_statement && matches!(kind, AstKind::ExpressionStatement(_)) {
return;
}
};
if unary_expr.operator == UnaryOperator::Void {
ctx.diagnostic(NoVoidDiagnostic(Span {
start: unary_expr.span.start,
end: unary_expr.span.start + 4,
}));
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
("var foo = bar()", None),
("foo.void()", None),
("foo.void = bar", None),
("delete foo;", None),
("void 0", Some(serde_json::json!([{ "allowAsStatement": true }]))),
("void(0)", Some(serde_json::json!([{ "allowAsStatement": true }]))),
];
let fail = vec![
("void 0", None),
("void 0", Some(serde_json::json!([{}]))),
("void 0", Some(serde_json::json!([{ "allowAsStatement": false }]))),
("void(0)", None),
("var foo = void 0", None),
("var foo = void 0", Some(serde_json::json!([{ "allowAsStatement": true }]))),
];
Tester::new(NoVoid::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,48 @@
---
source: crates/oxc_linter/src/tester.rs
assertion_line: 150
expression: no_void
---
⚠ eslint(no-void): Disallow `void` operators
╭─[no_void.tsx:1:1]
1 │ void 0
· ────
╰────
help: Expected 'undefined' and instead saw 'void'.
⚠ eslint(no-void): Disallow `void` operators
╭─[no_void.tsx:1:1]
1 │ void 0
· ────
╰────
help: Expected 'undefined' and instead saw 'void'.
⚠ eslint(no-void): Disallow `void` operators
╭─[no_void.tsx:1:1]
1 │ void 0
· ────
╰────
help: Expected 'undefined' and instead saw 'void'.
⚠ eslint(no-void): Disallow `void` operators
╭─[no_void.tsx:1:1]
1 │ void(0)
· ────
╰────
help: Expected 'undefined' and instead saw 'void'.
⚠ eslint(no-void): Disallow `void` operators
╭─[no_void.tsx:1:1]
1 │ var foo = void 0
· ────
╰────
help: Expected 'undefined' and instead saw 'void'.
⚠ eslint(no-void): Disallow `void` operators
╭─[no_void.tsx:1:1]
1 │ var foo = void 0
· ────
╰────
help: Expected 'undefined' and instead saw 'void'.