feat(linter): no-empty-pattern (#74)

* feat(linter): no-empty-pattern

* chore(linter/no-empty-pattern): update docs and examples

* chore: fix typo
This commit is contained in:
magic-akari 2023-03-03 17:53:00 +08:00 committed by GitHub
parent 92b232372f
commit 85de06eae7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 196 additions and 0 deletions

View file

@ -1,10 +1,12 @@
mod no_array_constructor;
mod no_debugger;
mod no_empty;
mod no_empty_pattern;
pub use no_array_constructor::NoArrayConstructor;
pub use no_debugger::NoDebugger;
pub use no_empty::NoEmpty;
pub use no_empty_pattern::NoEmptyPattern;
use crate::{context::LintContext, rule::Rule, rule::RuleMeta, AstNode};
@ -13,6 +15,7 @@ lazy_static::lazy_static! {
RuleEnum::NoDebugger(NoDebugger::default()),
RuleEnum::NoEmpty(NoEmpty::default()),
RuleEnum::NoArrayConstructor(NoArrayConstructor::default()),
RuleEnum::NoEmptyPattern(NoEmptyPattern::default()),
];
}
@ -22,6 +25,7 @@ pub enum RuleEnum {
NoDebugger(NoDebugger),
NoEmpty(NoEmpty),
NoArrayConstructor(NoArrayConstructor),
NoEmptyPattern(NoEmptyPattern),
}
impl RuleEnum {
@ -30,6 +34,7 @@ impl RuleEnum {
Self::NoDebugger(_) => NoDebugger::NAME,
Self::NoEmpty(_) => NoEmpty::NAME,
Self::NoArrayConstructor(_) => NoArrayConstructor::NAME,
Self::NoEmptyPattern(_) => NoEmptyPattern::NAME,
}
}
@ -44,6 +49,9 @@ impl RuleEnum {
Self::NoArrayConstructor(_) => Self::NoArrayConstructor(
maybe_value.map(NoArrayConstructor::from_configuration).unwrap_or_default(),
),
Self::NoEmptyPattern(_) => Self::NoEmptyPattern(
maybe_value.map(NoEmptyPattern::from_configuration).unwrap_or_default(),
),
}
}
@ -52,6 +60,7 @@ impl RuleEnum {
Self::NoDebugger(rule) => rule.run(node, ctx),
Self::NoEmpty(rule) => rule.run(node, ctx),
Self::NoArrayConstructor(rule) => rule.run(node, ctx),
Self::NoEmptyPattern(rule) => rule.run(node, ctx),
}
}
}

View file

@ -0,0 +1,119 @@
use oxc_ast::{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-empty-pattern): Disallow empty destructuring patterns")]
#[diagnostic()]
struct NoEmptyPatternDiagnostic(&'static str, #[label("Empty {0} binding pattern")] pub Span);
#[derive(Debug, Default, Clone)]
pub struct NoEmptyPattern;
declare_oxc_lint!(
/// ### What it does
/// Disallow empty destructuring patterns
///
/// ### Why is this bad?
/// When using destructuring, its possible to create a pattern that has no effect.
/// This happens when empty curly braces are used to the right of
/// an embedded object destructuring pattern, such as:
///
/// ```JavaScript
/// // doesn't create any variables
/// var {a: {}} = foo;
/// ```
/// In this code, no new variables are created because a is just a location helper
/// while the `{}` is expected to contain the variables to create, such as:
///
/// ```JavaScript
/// // creates variable b
/// var {a: { b }} = foo;
/// ```
///
/// In many cases, the empty object pattern is a mistake
/// where the author intended to use a default value instead, such as:
///
/// ```JavaScript
/// // creates variable a
/// var {a = {}} = foo;
/// ```
///
/// The difference between these two patterns is subtle,
/// especially because the problematic empty pattern looks just like an object literal.
///
/// ### Examples of incorrect code for this rule:
///
/// ```JavaScript
/// var {} = foo;
/// var [] = foo;
/// var {a: {}} = foo;
/// var {a: []} = foo;
/// function foo({}) {}
/// function foo([]) {}
/// function foo({a: {}}) {}
/// function foo({a: []}) {}
/// ```
///
/// ### Examples of correct code for this rule:
///
/// ```JavaScript
/// var {a = {}} = foo;
/// var {a = []} = foo;
/// function foo({a = {}}) {}
/// function foo({a = []}) {}
/// ```
///
NoEmptyPattern
);
const RULE_NAME: &str = "no-empty-pattern";
impl Rule for NoEmptyPattern {
const NAME: &'static str = RULE_NAME;
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let (pattern_type, span) = match node.get().kind() {
AstKind::ArrayPattern(array) if array.elements.is_empty() => ("array", array.span),
AstKind::ObjectPattern(object) if object.properties.is_empty() => {
("object", object.span)
}
_ => return,
};
ctx.diagnostic(NoEmptyPatternDiagnostic(pattern_type, span));
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
("var {a = {}} = foo;", None),
("var {a, b = {}} = foo;", None),
("var {a = []} = foo;", None),
("function foo({a = {}}) {}", None),
("function foo({a = []}) {}", None),
("var [a] = foo", None),
];
let fail = vec![
("var {} = foo", None),
("var [] = foo", None),
("var {a: {}} = foo", None),
("var {a, b: {}} = foo", None),
("var {a: []} = foo", None),
("function foo({}) {}", None),
("function foo([]) {}", None),
("function foo({a: {}}) {}", None),
("function foo({a: []}) {}", None),
];
Tester::new(RULE_NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,68 @@
---
source: crates/oxc_linter/src/tester.rs
expression: no_empty_pattern
---
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ var {} = foo
· ─┬
· ╰── Empty object binding pattern
╰────
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ var [] = foo
· ─┬
· ╰── Empty array binding pattern
╰────
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ var {a: {}} = foo
· ─┬
· ╰── Empty object binding pattern
╰────
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ var {a, b: {}} = foo
· ─┬
· ╰── Empty object binding pattern
╰────
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ var {a: []} = foo
· ─┬
· ╰── Empty array binding pattern
╰────
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ function foo({}) {}
· ─┬
· ╰── Empty object binding pattern
╰────
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ function foo([]) {}
· ─┬
· ╰── Empty array binding pattern
╰────
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ function foo({a: {}}) {}
· ─┬
· ╰── Empty object binding pattern
╰────
× eslint(no-empty-pattern): Disallow empty destructuring patterns
╭─[no_empty_pattern.tsx:1:1]
1 │ function foo({a: []}) {}
· ─┬
· ╰── Empty array binding pattern
╰────