feat(linter) eslint plugin unicorn: prefer optional catch binding (#1188)

This commit is contained in:
Cameron 2023-11-09 01:40:49 +00:00 committed by GitHub
parent 49b6be607e
commit e5d281e8e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 181 additions and 0 deletions

View file

@ -163,6 +163,7 @@ mod unicorn {
pub mod prefer_code_point;
pub mod prefer_date_now;
pub mod prefer_logical_operator_over_ternary;
pub mod prefer_optional_catch_binding;
pub mod prefer_query_selector;
pub mod prefer_regexp_test;
pub mod prefer_string_trim_start_end;
@ -301,6 +302,7 @@ oxc_macros::declare_all_lint_rules! {
unicorn::prefer_code_point,
unicorn::prefer_date_now,
unicorn::prefer_logical_operator_over_ternary,
unicorn::prefer_optional_catch_binding,
unicorn::prefer_query_selector,
unicorn::prefer_regexp_test,
unicorn::prefer_string_trim_start_end,

View file

@ -0,0 +1,119 @@
use oxc_ast::{
ast::{BindingPattern, BindingPatternKind},
AstKind,
};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused")]
#[diagnostic(severity(warning))]
struct PreferOptionalCatchBindingDiagnostic(#[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct PreferOptionalCatchBinding;
declare_oxc_lint!(
/// ### What it does
///
/// Prefers omitting the catch binding parameter if it is unused
///
/// ### Why is this bad?
///
/// It is unnecessary to bind the error to a variable if it is not used.
///
/// ### Example
/// ```javascript
/// // Bad
/// try {
/// // ...
/// } catch (e) { }
///
/// // Good
/// try {
/// // ...
/// } catch { }
/// ```
PreferOptionalCatchBinding,
style
);
impl Rule for PreferOptionalCatchBinding {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::CatchClause(catch_clause) = node.kind() else { return };
let Some(catch_param) = &catch_clause.param else { return };
let references_count = get_param_references_count(catch_param, ctx);
if references_count != 0 {
return;
}
ctx.diagnostic(PreferOptionalCatchBindingDiagnostic(catch_param.span()));
}
}
fn get_param_references_count(binding_pat: &BindingPattern, ctx: &LintContext) -> usize {
match &binding_pat.kind {
BindingPatternKind::BindingIdentifier(binding_ident) => {
ctx.semantic().symbol_references(binding_ident.symbol_id.get().unwrap()).count()
}
BindingPatternKind::ObjectPattern(object_pat) => {
let mut count = 0;
for prop in &object_pat.properties {
count += get_param_references_count(&prop.value, ctx);
}
if let Some(rest) = &object_pat.rest {
count += get_param_references_count(&rest.argument, ctx);
}
count
}
BindingPatternKind::AssignmentPattern(_) => 1,
BindingPatternKind::ArrayPattern(array_pat) => {
let mut count = 0;
for element in (&array_pat.elements).into_iter().flatten() {
count += get_param_references_count(element, ctx);
}
count
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
r#"try {} catch {}"#,
r#"try {} catch ({message}) {alert(message)}"#,
r#"try {} catch ({cause: {message}}) {alert(message)}"#,
r#"try {} catch({nonExistsProperty = thisWillExecute()}) {}"#,
];
let fail = vec![
r#"try {} catch (_) {}"#,
r#"try {} catch (theRealErrorName) {}"#,
r#"try { } catch (e)
{ }"#,
r#"try {} catch(e) {}"#,
r#"try {} catch (e){}"#,
r#"try {} catch ({}) {}"#,
r#"try {} catch ({message}) {}"#,
r#"try {} catch ({message: notUsedMessage}) {}"#,
r#"try {} catch ({cause: {message}}) {}"#,
];
Tester::new_without_config(PreferOptionalCatchBinding::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,60 @@
---
source: crates/oxc_linter/src/tester.rs
expression: prefer_optional_catch_binding
---
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try {} catch (_) {}
· ─
╰────
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try {} catch (theRealErrorName) {}
· ────────────────
╰────
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try { } catch (e)
· ─
2 │ { }
╰────
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try {} catch(e) {}
· ─
╰────
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try {} catch (e){}
· ─
╰────
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try {} catch ({}) {}
· ──
╰────
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try {} catch ({message}) {}
· ─────────
╰────
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try {} catch ({message: notUsedMessage}) {}
· ─────────────────────────
╰────
⚠ eslint-plugin-unicorn(prefer-optional-catch-binding): Prefer omitting the catch binding parameter if it is unused
╭─[prefer_optional_catch_binding.tsx:1:1]
1 │ try {} catch ({cause: {message}}) {}
· ──────────────────
╰────