feat(linter/eslint): Implement no-empty-function rule (#3181)

Rule Detail:
[link](https://eslint.org/docs/latest/rules/no-empty-function)

---------

Co-authored-by: Boshen <boshenc@gmail.com>
This commit is contained in:
Jelle van der Waa 2024-05-07 04:56:13 +02:00 committed by GitHub
parent 5514936f51
commit 5081652bc1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 289 additions and 0 deletions

View file

@ -69,6 +69,7 @@ mod eslint {
pub mod no_duplicate_case;
pub mod no_empty;
pub mod no_empty_character_class;
pub mod no_empty_function;
pub mod no_empty_pattern;
pub mod no_empty_static_block;
pub mod no_eq_null;
@ -427,6 +428,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::no_duplicate_case,
eslint::no_empty,
eslint::no_empty_character_class,
eslint::no_empty_function,
eslint::no_empty_pattern,
eslint::no_empty_static_block,
eslint::no_eval,

View file

@ -0,0 +1,155 @@
use oxc_ast::AstKind;
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint(no-empty-function): Disallow empty functions")]
#[diagnostic(severity(warning), help("Unexpected empty function block"))]
struct NoEmptyFunctionDiagnostic(#[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct NoEmptyFunction;
declare_oxc_lint!(
/// ### What it does
/// Disallows the usages of empty functions
///
/// ### Why is this bad?
/// Empty functions can reduce readability because readers need to guess whether its
/// intentional or not. So writing a clear comment for empty functions is a good practice.
///
/// ### Example
/// ```javascript
///
/// function foo() {
/// }
///
/// const bar = () => {};
///
/// ```
NoEmptyFunction,
restriction,
);
impl Rule for NoEmptyFunction {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::FunctionBody(fb) = node.kind() {
if fb.is_empty() && !ctx.semantic().trivias().has_comments_between(fb.span) {
ctx.diagnostic(NoEmptyFunctionDiagnostic(fb.span));
}
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
"
function foo() {
// empty
}
",
"
function* baz() {
// empty
}
",
"
const bar = () => {
// empty
};
",
"
const obj = {
foo: function() {
// empty
},
bar: function*() {
// empty
},
foobar() {
// empty
}
};
",
"
class A {
constructor() {
// empty
}
foo() {
// empty
}
*foo1() {
// empty
}
get bar() {
// empty
}
set bar(value) {
// empty
}
static bar() {
// empty
}
static *barr() {
// empty
}
static get baz() {
// empty
}
static set baz(value) {
// empty
}
}
",
];
let fail = vec![
"function foo() {}",
"const bar = () => {};",
"function* baz() {}",
"
const obj = {
foo: function() {
},
bar: function*() {
},
foobar() {
}
};
",
"
class A {
constructor() {
}
foo() {
}
*foo1() {
}
get fooz() {
}
set fooz(value) {
}
static bar() {
}
static *barr() {
}
static get baz() {
}
static set baz(value) {
}
}
",
];
Tester::new(NoEmptyFunction::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,132 @@
---
source: crates/oxc_linter/src/tester.rs
expression: no_empty_function
---
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:1:16]
1 │ function foo() {}
· ──
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:1:19]
1 │ const bar = () => {};
· ──
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:1:17]
1 │ function* baz() {}
· ──
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:3:29]
2 │ const obj = {
3 │ ╭─▶ foo: function() {
4 │ ╰─▶ },
5 │ bar: function*() {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:5:30]
4 │ },
5 │ ╭─▶ bar: function*() {
6 │ ╰─▶ },
7 │ foobar() {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:7:22]
6 │ },
7 │ ╭─▶ foobar() {
8 │ ╰─▶ }
9 │ };
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:3:27]
2 │ class A {
3 │ ╭─▶ constructor() {
4 │ ╰─▶ }
5 │ foo() {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:5:19]
4 │ }
5 │ ╭─▶ foo() {
6 │ ╰─▶ }
7 │ *foo1() {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:7:21]
6 │ }
7 │ ╭─▶ *foo1() {
8 │ ╰─▶ }
9 │ get fooz() {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:9:24]
8 │ }
9 │ ╭─▶ get fooz() {
10 │ ╰─▶ }
11 │ set fooz(value) {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:11:29]
10 │ }
11 │ ╭─▶ set fooz(value) {
12 │ ╰─▶ }
13 │ static bar() {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:13:26]
12 │ }
13 │ ╭─▶ static bar() {
14 │ ╰─▶ }
15 │ static *barr() {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:15:28]
14 │ }
15 │ ╭─▶ static *barr() {
16 │ ╰─▶ }
17 │ static get baz() {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:17:30]
16 │ }
17 │ ╭─▶ static get baz() {
18 │ ╰─▶ }
19 │ static set baz(value) {
╰────
help: Unexpected empty function block
⚠ eslint(no-empty-function): Disallow empty functions
╭─[no_empty_function.tsx:19:35]
18 │ }
19 │ ╭─▶ static set baz(value) {
20 │ ╰─▶ }
21 │ }
╰────
help: Unexpected empty function block